Natively handle negative numbers in tokenizer instead of the neg() function.

This commit is contained in:
Stephen Chung 2020-03-09 10:10:19 +08:00
parent c5b40783ef
commit 01d04f717b
3 changed files with 46 additions and 24 deletions

View File

@ -338,6 +338,7 @@ impl Token {
use self::Token::*; use self::Token::*;
match *self { match *self {
LexError(_) |
LeftBrace | // (+expr) - is unary LeftBrace | // (+expr) - is unary
// RightBrace | {expr} - expr not unary & is closing // RightBrace | {expr} - expr not unary & is closing
LeftParen | // {-expr} - is unary LeftParen | // {-expr} - is unary
@ -386,6 +387,7 @@ impl Token {
PowerOf | PowerOf |
In | In |
PowerOfAssign => true, PowerOfAssign => true,
_ => false, _ => false,
} }
} }
@ -398,6 +400,7 @@ impl Token {
RightBrace | RightParen | RightBracket | Plus | Minus | Multiply | Divide | Comma RightBrace | RightParen | RightBracket | Plus | Minus | Multiply | Divide | Comma
| Equals | LessThan | GreaterThan | LessThanEqualsTo | GreaterThanEqualsTo | Equals | LessThan | GreaterThan | LessThanEqualsTo | GreaterThanEqualsTo
| EqualsTo | NotEqualsTo | Pipe | Or | Ampersand | And | PowerOf => true, | EqualsTo | NotEqualsTo | Pipe | Or | Ampersand | And | PowerOf => true,
_ => false, _ => false,
} }
} }
@ -567,6 +570,8 @@ impl<'a> TokenIterator<'a> {
} }
fn inner_next(&mut self) -> Option<(Token, Position)> { fn inner_next(&mut self) -> Option<(Token, Position)> {
let mut negated = false;
while let Some(c) = self.char_stream.next() { while let Some(c) = self.char_stream.next() {
self.advance(); self.advance();
@ -653,6 +658,10 @@ impl<'a> TokenIterator<'a> {
} }
} }
if negated {
result.insert(0, '-');
}
if let Some(radix) = radix_base { if let Some(radix) = radix_base {
let out: String = result.iter().skip(2).filter(|&&c| c != '_').collect(); let out: String = result.iter().skip(2).filter(|&&c| c != '_').collect();
@ -761,20 +770,17 @@ impl<'a> TokenIterator<'a> {
pos, pos,
)) ))
} }
'-' => { '-' => match self.char_stream.peek() {
return Some(( // Negative number?
match self.char_stream.peek() { Some('0'..='9') => negated = true,
Some(&'=') => { Some('=') => {
self.char_stream.next(); self.char_stream.next();
self.advance(); self.advance();
Token::MinusAssign return Some((Token::MinusAssign, pos));
} }
_ if self.last.is_next_unary() => Token::UnaryMinus, _ if self.last.is_next_unary() => return Some((Token::UnaryMinus, pos)),
_ => Token::Minus, _ => return Some((Token::Minus, pos)),
}, },
pos,
))
}
'*' => { '*' => {
return Some(( return Some((
match self.char_stream.peek() { match self.char_stream.peek() {
@ -1034,19 +1040,28 @@ fn get_precedence(token: &Token) -> i8 {
| Token::XOrAssign | Token::XOrAssign
| Token::ModuloAssign | Token::ModuloAssign
| Token::PowerOfAssign => 10, | Token::PowerOfAssign => 10,
Token::Or | Token::XOr | Token::Pipe => 11, Token::Or | Token::XOr | Token::Pipe => 11,
Token::And | Token::Ampersand => 12, Token::And | Token::Ampersand => 12,
Token::LessThan Token::LessThan
| Token::LessThanEqualsTo | Token::LessThanEqualsTo
| Token::GreaterThan | Token::GreaterThan
| Token::GreaterThanEqualsTo | Token::GreaterThanEqualsTo
| Token::EqualsTo | Token::EqualsTo
| Token::NotEqualsTo => 15, | Token::NotEqualsTo => 15,
Token::Plus | Token::Minus => 20, Token::Plus | Token::Minus => 20,
Token::Divide | Token::Multiply | Token::PowerOf => 40, Token::Divide | Token::Multiply | Token::PowerOf => 40,
Token::LeftShift | Token::RightShift => 50, Token::LeftShift | Token::RightShift => 50,
Token::Modulo => 60, Token::Modulo => 60,
Token::Period => 100, Token::Period => 100,
_ => -1, _ => -1,
} }
} }
@ -1056,6 +1071,7 @@ fn parse_paren_expr<'a>(
begin: Position, begin: Position,
) -> Result<Expr, ParseError> { ) -> Result<Expr, ParseError> {
match input.peek() { match input.peek() {
// ()
Some((Token::RightParen, _)) => { Some((Token::RightParen, _)) => {
input.next(); input.next();
return Ok(Expr::Unit(begin)); return Ok(Expr::Unit(begin));
@ -1272,16 +1288,22 @@ fn parse_unary<'a>(input: &mut Peekable<TokenIterator<'a>>) -> Result<Expr, Pars
Some(&(Token::UnaryMinus, pos)) => { Some(&(Token::UnaryMinus, pos)) => {
input.next(); input.next();
Ok(Expr::FunctionCall( match parse_unary(input) {
"-".into(), // Negative integer
vec![parse_primary(input)?], Ok(Expr::IntegerConstant(i, pos)) => Ok(i
None, .checked_neg()
pos, .map(|x| Expr::IntegerConstant(x, pos))
)) .unwrap_or_else(|| Expr::FloatConstant(-(i as f64), pos))),
// Negative float
Ok(Expr::FloatConstant(f, pos)) => Ok(Expr::FloatConstant(-f, pos)),
// Call negative function
Ok(expr) => Ok(Expr::FunctionCall("-".into(), vec![expr], None, pos)),
err @ Err(_) => err,
}
} }
Some(&(Token::UnaryPlus, _)) => { Some(&(Token::UnaryPlus, _)) => {
input.next(); input.next();
parse_primary(input) parse_unary(input)
} }
Some(&(Token::Bang, pos)) => { Some(&(Token::Bang, pos)) => {
input.next(); input.next();

View File

@ -21,7 +21,7 @@ fn test_math() -> Result<(), EvalAltResult> {
Err(EvalAltResult::ErrorArithmetic(_, _)) => (), Err(EvalAltResult::ErrorArithmetic(_, _)) => (),
r => panic!("should return overflow error: {:?}", r), r => panic!("should return overflow error: {:?}", r),
} }
match engine.eval::<i64>("(-9223372036854775807) - 2") { match engine.eval::<i64>("-9223372036854775808 - 1") {
Err(EvalAltResult::ErrorArithmetic(_, _)) => (), Err(EvalAltResult::ErrorArithmetic(_, _)) => (),
r => panic!("should return underflow error: {:?}", r), r => panic!("should return underflow error: {:?}", r),
} }

View File

@ -5,8 +5,8 @@ fn test_unary_minus() -> Result<(), EvalAltResult> {
let mut engine = Engine::new(); let mut engine = Engine::new();
assert_eq!(engine.eval::<i64>("let x = -5; x")?, -5); assert_eq!(engine.eval::<i64>("let x = -5; x")?, -5);
assert_eq!(engine.eval::<i64>("fn n(x) { -x } n(5)")?, -5); assert_eq!(engine.eval::<i64>("fn neg(x) { -x } neg(5)")?, -5);
assert_eq!(engine.eval::<i64>("5 - -(-5)")?, 0); assert_eq!(engine.eval::<i64>("5 - -+++--+-5")?, 0);
Ok(()) Ok(())
} }