diff --git a/src/engine.rs b/src/engine.rs index 0d163813..76057cb4 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -7,7 +7,7 @@ use std::fmt; use parser::{lex, parse, Expr, Stmt, FnDef}; use fn_register::FnRegister; -use std::ops::{Add, Sub, Mul, Div}; +use std::ops::{Add, Sub, Mul, Div, Neg}; use std::cmp::{Ord, Eq}; #[derive(Debug)] @@ -1317,6 +1317,14 @@ impl Engine { ) } + macro_rules! reg_un { + ($engine:expr, $x:expr, $op:expr, $( $y:ty ),*) => ( + $( + $engine.register_fn($x, ($op as fn(x: $y)->$y)); + )* + ) + } + macro_rules! reg_cmp { ($engine:expr, $x:expr, $op:expr, $( $y:ty ),*) => ( $( @@ -1329,6 +1337,7 @@ impl Engine { fn sub(x: T, y: T) -> ::Output { x - y } fn mul(x: T, y: T) -> ::Output { x * y } fn div(x: T, y: T) -> ::Output { x / y } + fn neg(x: T) -> ::Output { -x } fn lt(x: T, y: T) -> bool { x < y } fn lte(x: T, y: T) -> bool { x <= y } fn gt(x: T, y: T) -> bool { x > y } @@ -1337,6 +1346,7 @@ impl Engine { fn ne(x: T, y: T) -> bool { x != y } fn and(x: bool, y: bool) -> bool { x && y } fn or(x: bool, y: bool) -> bool { x || y } + fn not(x: bool) -> bool { !x } fn concat(x: String, y: String) -> String { x + &y } reg_op!(engine, "+", add, i32, i64, u32, u64, f32, f64); @@ -1354,6 +1364,9 @@ impl Engine { reg_op!(engine, "||", or, bool); reg_op!(engine, "&&", and, bool); + reg_un!(engine, "-", neg, i32, i64, f32, f64); + reg_un!(engine, "!", not, bool); + engine.register_fn("+", concat); // engine.register_fn("[]", idx); diff --git a/src/parser.rs b/src/parser.rs index 18d3fe72..701ab509 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -4,12 +4,13 @@ use std::iter::Peekable; use std::str::Chars; use std::char; -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum LexError { UnexpectedChar, MalformedEscapeSequence, MalformedNumber, MalformedChar, + Nothing } impl Error for LexError { @@ -19,6 +20,7 @@ impl Error for LexError { LexError::MalformedEscapeSequence => "Unexpected values in escape sequence", LexError::MalformedNumber => "Unexpected characters in number", LexError::MalformedChar => "Char constant not a single character", + LexError::Nothing => "This error is for internal use only" } } @@ -111,7 +113,7 @@ pub enum Expr { False, } -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum Token { IntConst(i64), Identifier(String), @@ -124,7 +126,9 @@ pub enum Token { LSquare, RSquare, Plus, + UnaryPlus, Minus, + UnaryMinus, Multiply, Divide, Semicolon, @@ -155,7 +159,91 @@ pub enum Token { LexErr(LexError), } +impl Token { + // if another operator is after these, it's probably an unary operator + // not sure about fn's name + pub fn is_next_unary(&self) -> bool { + use self::Token::*; + + match *self { + LCurly | // (+expr) - is unary + // RCurly | {expr} - expr not unary & is closing + LParen | // {-expr} - is unary + // RParen | (expr) - expr not unary & is closing + LSquare | // [-expr] - is unary + // RSquare | [expr] - expr not unary & is closing + Plus | + UnaryPlus | + Minus | + UnaryMinus | + Multiply | + Divide | + Colon | + Comma | + Period | + Equals | + LessThan | + GreaterThan | + Bang | + LessThanEqual | + GreaterThanEqual | + EqualTo | + NotEqualTo | + Pipe | + Or | + Ampersand | + And | + Return => true, + _ => false, + } + } + + #[allow(dead_code)] + pub fn is_bin_op(&self) -> bool { + use self::Token::*; + + match *self { + RCurly | + RParen | + RSquare | + Plus | + Minus | + Multiply | + Divide | + Comma | + // Period | <- does period count? + Equals | + LessThan | + GreaterThan | + LessThanEqual | + GreaterThanEqual | + EqualTo | + NotEqualTo | + Pipe | + Or | + Ampersand | + And => true, + _ => false, + } + } + + #[allow(dead_code)] + pub fn is_un_op(&self) -> bool { + use self::Token::*; + + match *self { + UnaryPlus | + UnaryMinus | + Equals | + Bang | + Return => true, + _ => false, + } + } +} + pub struct TokenIterator<'a> { + last: Token, char_stream: Peekable>, } @@ -262,12 +350,8 @@ impl<'a> TokenIterator<'a> { let out: String = result.iter().cloned().collect(); Ok(out) } -} -impl<'a> Iterator for TokenIterator<'a> { - type Item = Token; - - fn next(&mut self) -> Option { + fn inner_next(&mut self) -> Option { while let Some(c) = self.char_stream.next() { match c { '0'...'9' => { @@ -350,8 +434,14 @@ impl<'a> Iterator for TokenIterator<'a> { ')' => return Some(Token::RParen), '[' => return Some(Token::LSquare), ']' => return Some(Token::RSquare), - '+' => return Some(Token::Plus), - '-' => return Some(Token::Minus), + '+' => { + if self.last.is_next_unary() { return Some(Token::UnaryPlus) } + else { return Some(Token::Plus) } + } + '-' => { + if self.last.is_next_unary() { return Some(Token::UnaryMinus) } + else { return Some(Token::Minus) } + } '*' => return Some(Token::Multiply), '/' => { match self.char_stream.peek() { @@ -450,8 +540,21 @@ impl<'a> Iterator for TokenIterator<'a> { } } +impl<'a> Iterator for TokenIterator<'a> { + type Item = Token; + + // TODO - perhaps this could be optimized to only keep track of last for operators? + fn next(&mut self) -> Option { + self.last = match self.inner_next() { + Some(c) => c, + None => return None, + }; + Some(self.last.clone()) + } +} + pub fn lex(input: &str) -> TokenIterator { - TokenIterator { char_stream: input.chars().peekable() } + TokenIterator { last: Token::LexErr(LexError::Nothing), char_stream: input.chars().peekable() } } fn get_precedence(token: &Token) -> i32 { @@ -599,6 +702,20 @@ fn parse_primary<'a>(input: &mut Peekable>) -> Result(input: &mut Peekable>) -> Result { + let tok = match input.peek() { + Some(tok) => tok.clone(), + None => return Err(ParseError::InputPastEndOfFile), + }; + + match tok { + Token::UnaryMinus => { input.next(); Ok(Expr::FnCall("-".to_string(), vec![parse_primary(input)?])) } + Token::UnaryPlus => { input.next(); parse_primary(input) } + Token::Bang => { input.next(); Ok(Expr::FnCall("!".to_string(), vec![parse_primary(input)?])) } + _ => parse_primary(input) + } +} + fn parse_binop<'a>(input: &mut Peekable>, prec: i32, lhs: Expr) @@ -617,7 +734,7 @@ fn parse_binop<'a>(input: &mut Peekable>, } if let Some(op_token) = input.next() { - let mut rhs = try!(parse_primary(input)); + let mut rhs = try!(parse_unary(input)); let mut next_prec = -1; @@ -658,7 +775,7 @@ fn parse_binop<'a>(input: &mut Peekable>, } fn parse_expr<'a>(input: &mut Peekable>) -> Result { - let lhs = try!(parse_primary(input)); + let lhs = try!(parse_unary(input)); parse_binop(input, 0, lhs) }