implement unary + and -, add useful functions to Token
This commit is contained in:
parent
7f4fbd940a
commit
679f0de6b6
@ -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<T: Sub>(x: T, y: T) -> <T as Sub>::Output { x - y }
|
||||
fn mul<T: Mul>(x: T, y: T) -> <T as Mul>::Output { x * y }
|
||||
fn div<T: Div>(x: T, y: T) -> <T as Div>::Output { x / y }
|
||||
fn neg<T: Neg>(x: T) -> <T as Neg>::Output { -x }
|
||||
fn lt<T: Ord>(x: T, y: T) -> bool { x < y }
|
||||
fn lte<T: Ord>(x: T, y: T) -> bool { x <= y }
|
||||
fn gt<T: Ord>(x: T, y: T) -> bool { x > y }
|
||||
@ -1337,6 +1346,7 @@ impl Engine {
|
||||
fn ne<T: Eq>(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);
|
||||
|
141
src/parser.rs
141
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<Chars<'a>>,
|
||||
}
|
||||
|
||||
@ -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<Self::Item> {
|
||||
fn inner_next(&mut self) -> Option<Token> {
|
||||
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::Item> {
|
||||
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<TokenIterator<'a>>) -> Result<Expr, Pa
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_unary<'a>(input: &mut Peekable<TokenIterator<'a>>) -> Result<Expr, ParseError> {
|
||||
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<TokenIterator<'a>>,
|
||||
prec: i32,
|
||||
lhs: Expr)
|
||||
@ -617,7 +734,7 @@ fn parse_binop<'a>(input: &mut Peekable<TokenIterator<'a>>,
|
||||
}
|
||||
|
||||
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<TokenIterator<'a>>,
|
||||
}
|
||||
|
||||
fn parse_expr<'a>(input: &mut Peekable<TokenIterator<'a>>) -> Result<Expr, ParseError> {
|
||||
let lhs = try!(parse_primary(input));
|
||||
let lhs = try!(parse_unary(input));
|
||||
|
||||
parse_binop(input, 0, lhs)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user