Add Token::Unit.

This commit is contained in:
Stephen Chung 2022-04-21 10:04:46 +08:00
parent 299d6ef308
commit 5e4f27ae25
5 changed files with 69 additions and 34 deletions

View File

@ -832,6 +832,7 @@ impl Expr {
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Token::LeftBracket => true, Token::LeftBracket => true,
Token::LeftParen => true, Token::LeftParen => true,
Token::Unit => true,
Token::Bang => true, Token::Bang => true,
Token::DoubleColon => true, Token::DoubleColon => true,
_ => false, _ => false,

View File

@ -426,10 +426,6 @@ impl Engine {
let mut settings = settings; let mut settings = settings;
settings.pos = eat_token(input, Token::LeftParen); settings.pos = eat_token(input, Token::LeftParen);
if match_token(input, Token::RightParen).0 {
return Ok(Expr::Unit(settings.pos));
}
let expr = self.parse_expr(input, state, lib, settings.level_up())?; let expr = self.parse_expr(input, state, lib, settings.level_up())?;
match input.next().expect(NEVER_ENDS) { match input.next().expect(NEVER_ENDS) {
@ -453,6 +449,7 @@ impl Engine {
state: &mut ParseState, state: &mut ParseState,
lib: &mut FnLib, lib: &mut FnLib,
id: Identifier, id: Identifier,
no_args: bool,
capture_parent_scope: bool, capture_parent_scope: bool,
#[cfg(not(feature = "no_module"))] namespace: crate::ast::Namespace, #[cfg(not(feature = "no_module"))] namespace: crate::ast::Namespace,
settings: ParseSettings, settings: ParseSettings,
@ -460,7 +457,11 @@ impl Engine {
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
settings.ensure_level_within_max_limit(state.max_expr_depth)?; settings.ensure_level_within_max_limit(state.max_expr_depth)?;
let (token, token_pos) = input.peek().expect(NEVER_ENDS); let (token, token_pos) = if no_args {
&(Token::RightParen, Position::NONE)
} else {
input.peek().expect(NEVER_ENDS)
};
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
let mut namespace = namespace; let mut namespace = namespace;
@ -479,7 +480,9 @@ impl Engine {
Token::LexError(err) => return Err(err.clone().into_err(*token_pos)), Token::LexError(err) => return Err(err.clone().into_err(*token_pos)),
// id() // id()
Token::RightParen => { Token::RightParen => {
eat_token(input, Token::RightParen); if !no_args {
eat_token(input, Token::RightParen);
}
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
let hash = if !namespace.is_empty() { let hash = if !namespace.is_empty() {
@ -897,7 +900,7 @@ impl Engine {
} }
let (name, pos) = match input.next().expect(NEVER_ENDS) { let (name, pos) = match input.next().expect(NEVER_ENDS) {
(Token::Identifier(s), pos) | (Token::StringConstant(s), pos) => { (Token::Identifier(s) | Token::StringConstant(s), pos) => {
if map.iter().any(|(p, ..)| **p == s) { if map.iter().any(|(p, ..)| **p == s) {
return Err(PERR::DuplicatedProperty(s.to_string()).into_err(pos)); return Err(PERR::DuplicatedProperty(s.to_string()).into_err(pos));
} }
@ -1201,6 +1204,11 @@ impl Engine {
let root_expr = match token { let root_expr = match token {
Token::EOF => return Err(PERR::UnexpectedEOF.into_err(settings.pos)), Token::EOF => return Err(PERR::UnexpectedEOF.into_err(settings.pos)),
Token::Unit => {
input.next();
Expr::Unit(settings.pos)
}
Token::IntegerConstant(..) Token::IntegerConstant(..)
| Token::CharConstant(..) | Token::CharConstant(..)
| Token::StringConstant(..) | Token::StringConstant(..)
@ -1218,13 +1226,13 @@ impl Engine {
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
Token::FloatConstant(x) => { Token::FloatConstant(x) => {
let x = *x; let x = *x;
input.next().expect(NEVER_ENDS); input.next();
Expr::FloatConstant(x, settings.pos) Expr::FloatConstant(x, settings.pos)
} }
#[cfg(feature = "decimal")] #[cfg(feature = "decimal")]
Token::DecimalConstant(x) => { Token::DecimalConstant(x) => {
let x = (*x).into(); let x = (*x).into();
input.next().expect(NEVER_ENDS); input.next();
Expr::DynamicConstant(Box::new(x), settings.pos) Expr::DynamicConstant(Box::new(x), settings.pos)
} }
@ -1235,6 +1243,7 @@ impl Engine {
stmt => unreachable!("Stmt::Block expected but gets {:?}", stmt), stmt => unreachable!("Stmt::Block expected but gets {:?}", stmt),
} }
} }
// ( - grouped expression // ( - grouped expression
Token::LeftParen => self.parse_paren_expr(input, state, lib, settings.level_up())?, Token::LeftParen => self.parse_paren_expr(input, state, lib, settings.level_up())?,
@ -1401,7 +1410,7 @@ impl Engine {
match input.peek().expect(NEVER_ENDS).0 { match input.peek().expect(NEVER_ENDS).0 {
// Function call // Function call
Token::LeftParen | Token::Bang => { Token::LeftParen | Token::Bang | Token::Unit => {
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
{ {
// Once the identifier consumed we must enable next variables capturing // Once the identifier consumed we must enable next variables capturing
@ -1467,11 +1476,13 @@ impl Engine {
match input.peek().expect(NEVER_ENDS).0 { match input.peek().expect(NEVER_ENDS).0 {
// Function call is allowed to have reserved keyword // Function call is allowed to have reserved keyword
Token::LeftParen | Token::Bang if is_keyword_function(&s) => Expr::Variable( Token::LeftParen | Token::Bang | Token::Unit if is_keyword_function(&s) => {
(None, ns, 0, state.get_identifier("", s)).into(), Expr::Variable(
None, (None, ns, 0, state.get_identifier("", s)).into(),
settings.pos, None,
), settings.pos,
)
}
// Access to `this` as a variable is OK within a function scope // Access to `this` as a variable is OK within a function scope
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
_ if &*s == KEYWORD_THIS && settings.is_function_scope => Expr::Variable( _ if &*s == KEYWORD_THIS && settings.is_function_scope => Expr::Variable(
@ -1531,30 +1542,33 @@ impl Engine {
// Qualified function call with ! // Qualified function call with !
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
(Expr::Variable(x, ..), Token::Bang) if !x.1.is_empty() => { (Expr::Variable(x, ..), Token::Bang) if !x.1.is_empty() => {
return if !match_token(input, Token::LeftParen).0 { return match input.peek().expect(NEVER_ENDS) {
Err(LexError::UnexpectedInput(Token::Bang.syntax().to_string()) (Token::LeftParen | Token::Unit, ..) => {
.into_err(tail_pos)) Err(LexError::UnexpectedInput(Token::Bang.syntax().to_string())
} else { .into_err(tail_pos))
Err(LexError::ImproperSymbol( }
_ => Err(LexError::ImproperSymbol(
"!".to_string(), "!".to_string(),
"'!' cannot be used to call module functions".to_string(), "'!' cannot be used to call module functions".to_string(),
) )
.into_err(tail_pos)) .into_err(tail_pos)),
}; };
} }
// Function call with ! // Function call with !
(Expr::Variable(x, .., pos), Token::Bang) => { (Expr::Variable(x, .., pos), Token::Bang) => {
match match_token(input, Token::LeftParen) { match input.peek().expect(NEVER_ENDS) {
(false, pos) => { (Token::LeftParen | Token::Unit, ..) => (),
(_, pos) => {
return Err(PERR::MissingToken( return Err(PERR::MissingToken(
Token::LeftParen.syntax().into(), Token::LeftParen.syntax().into(),
"to start arguments list of function call".into(), "to start arguments list of function call".into(),
) )
.into_err(pos)) .into_err(*pos))
} }
_ => (),
} }
let no_args = input.next().expect(NEVER_ENDS).0 == Token::Unit;
let (.., _ns, _, name) = *x; let (.., _ns, _, name) = *x;
settings.pos = pos; settings.pos = pos;
self.parse_fn_call( self.parse_fn_call(
@ -1562,6 +1576,7 @@ impl Engine {
state, state,
lib, lib,
name, name,
no_args,
true, true,
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
_ns, _ns,
@ -1569,7 +1584,7 @@ impl Engine {
)? )?
} }
// Function call // Function call
(Expr::Variable(x, .., pos), Token::LeftParen) => { (Expr::Variable(x, .., pos), t @ (Token::LeftParen | Token::Unit)) => {
let (.., _ns, _, name) = *x; let (.., _ns, _, name) = *x;
settings.pos = pos; settings.pos = pos;
self.parse_fn_call( self.parse_fn_call(
@ -1577,6 +1592,7 @@ impl Engine {
state, state,
lib, lib,
name, name,
t == Token::Unit,
false, false,
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
_ns, _ns,
@ -1992,7 +2008,7 @@ impl Engine {
)) ))
} }
// lhs.dot_lhs.dot_rhs or lhs.dot_lhs[idx_rhs] // lhs.dot_lhs.dot_rhs or lhs.dot_lhs[idx_rhs]
(lhs, rhs @ Expr::Dot(..)) | (lhs, rhs @ Expr::Index(..)) => { (lhs, rhs @ (Expr::Dot(..) | Expr::Index(..))) => {
let (x, term, pos, is_dot) = match rhs { let (x, term, pos, is_dot) = match rhs {
Expr::Dot(x, term, pos) => (x, term, pos, true), Expr::Dot(x, term, pos) => (x, term, pos, true),
Expr::Index(x, term, pos) => (x, term, pos, false), Expr::Index(x, term, pos) => (x, term, pos, false),
@ -2324,7 +2340,7 @@ impl Engine {
} }
} }
CUSTOM_SYNTAX_MARKER_BOOL => match input.next().expect(NEVER_ENDS) { CUSTOM_SYNTAX_MARKER_BOOL => match input.next().expect(NEVER_ENDS) {
(b @ Token::True, pos) | (b @ Token::False, pos) => { (b @ (Token::True | Token::False), pos) => {
inputs.push(Expr::BoolConstant(b == Token::True, pos)); inputs.push(Expr::BoolConstant(b == Token::True, pos));
segments.push(state.get_interned_string("", b.literal_syntax())); segments.push(state.get_interned_string("", b.literal_syntax()));
tokens.push(state.get_identifier("", CUSTOM_SYNTAX_MARKER_BOOL)); tokens.push(state.get_identifier("", CUSTOM_SYNTAX_MARKER_BOOL));
@ -2999,7 +3015,7 @@ impl Engine {
comments.push(comment); comments.push(comment);
match input.peek().expect(NEVER_ENDS) { match input.peek().expect(NEVER_ENDS) {
(Token::Fn, ..) | (Token::Private, ..) => break, (Token::Fn | Token::Private, ..) => break,
(Token::Comment(..), ..) => (), (Token::Comment(..), ..) => (),
_ => return Err(PERR::WrongDocComment.into_err(comments_pos)), _ => return Err(PERR::WrongDocComment.into_err(comments_pos)),
} }
@ -3269,14 +3285,21 @@ impl Engine {
Err(_) => return Err(PERR::FnMissingName.into_err(pos)), Err(_) => return Err(PERR::FnMissingName.into_err(pos)),
}; };
match input.peek().expect(NEVER_ENDS) { let no_params = match input.peek().expect(NEVER_ENDS) {
(Token::LeftParen, ..) => eat_token(input, Token::LeftParen), (Token::LeftParen, ..) => {
eat_token(input, Token::LeftParen);
match_token(input, Token::RightParen).0
}
(Token::Unit, ..) => {
eat_token(input, Token::Unit);
true
}
(.., pos) => return Err(PERR::FnMissingParams(name.to_string()).into_err(*pos)), (.., pos) => return Err(PERR::FnMissingParams(name.to_string()).into_err(*pos)),
}; };
let mut params = StaticVec::new_const(); let mut params = StaticVec::new_const();
if !match_token(input, Token::RightParen).0 { if !no_params {
let sep_err = format!("to separate the parameters of function '{}'", name); let sep_err = format!("to separate the parameters of function '{}'", name);
loop { loop {

View File

@ -382,6 +382,8 @@ pub enum Token {
LeftBracket, LeftBracket,
/// `]` /// `]`
RightBracket, RightBracket,
/// `()`
Unit,
/// `+` /// `+`
Plus, Plus,
/// `+` (unary) /// `+` (unary)
@ -558,6 +560,7 @@ impl Token {
RightParen => ")", RightParen => ")",
LeftBracket => "[", LeftBracket => "[",
RightBracket => "]", RightBracket => "]",
Unit => "()",
Plus => "+", Plus => "+",
UnaryPlus => "+", UnaryPlus => "+",
Minus => "-", Minus => "-",
@ -754,6 +757,7 @@ impl Token {
")" => RightParen, ")" => RightParen,
"[" => LeftBracket, "[" => LeftBracket,
"]" => RightBracket, "]" => RightBracket,
"()" => Unit,
"+" => Plus, "+" => Plus,
"-" => Minus, "-" => Minus,
"*" => Multiply, "*" => Multiply,
@ -1702,6 +1706,12 @@ fn get_next_token_inner(
('{', ..) => return Some((Token::LeftBrace, start_pos)), ('{', ..) => return Some((Token::LeftBrace, start_pos)),
('}', ..) => return Some((Token::RightBrace, start_pos)), ('}', ..) => return Some((Token::RightBrace, start_pos)),
// Unit
('(', ')') => {
eat_next(stream, pos);
return Some((Token::Unit, start_pos));
}
// Parentheses // Parentheses
('(', '*') => { ('(', '*') => {
eat_next(stream, pos); eat_next(stream, pos);

View File

@ -51,7 +51,8 @@ impl fmt::Display for LexError {
Self::ImproperSymbol(s, d) if d.is_empty() => { Self::ImproperSymbol(s, d) if d.is_empty() => {
write!(f, "Invalid symbol encountered: '{}'", s) write!(f, "Invalid symbol encountered: '{}'", s)
} }
Self::ImproperSymbol(.., d) => f.write_str(d), Self::ImproperSymbol(s, d) if s.is_empty() => f.write_str(d),
Self::ImproperSymbol(s, d) => write!(f, "{}: '{}'", d, s),
} }
} }
} }

View File

@ -17,6 +17,6 @@ fn test_unit_eq() -> Result<(), Box<EvalAltResult>> {
#[test] #[test]
fn test_unit_with_spaces() -> Result<(), Box<EvalAltResult>> { fn test_unit_with_spaces() -> Result<(), Box<EvalAltResult>> {
let engine = Engine::new(); let engine = Engine::new();
engine.run("let x = ( ); x")?; engine.run("let x = ( ); x").expect_err("should error");
Ok(()) Ok(())
} }