Operator-precedence parsing + tests
The tests are crippled now, because it's hard to write a test macro that can also match on Strings
This commit is contained in:
parent
032d01c9f5
commit
b1163e2ae4
@ -71,7 +71,7 @@ impl Parser {
|
|||||||
Parser { tokens: tokens }
|
Parser { tokens: tokens }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn peek(&mut self) -> Option<Token> {
|
fn peek(&self) -> Option<Token> {
|
||||||
self.tokens.last().map(|x| x.clone())
|
self.tokens.last().map(|x| x.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -79,7 +79,7 @@ impl Parser {
|
|||||||
self.tokens.pop()
|
self.tokens.pop()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_precedence(op: Op) -> Precedence {
|
fn get_precedence(&self, op: &Op) -> Precedence {
|
||||||
match &op.repr[..] {
|
match &op.repr[..] {
|
||||||
"+" => 10,
|
"+" => 10,
|
||||||
"-" => 10,
|
"-" => 10,
|
||||||
@ -208,8 +208,28 @@ impl Parser {
|
|||||||
self.precedence_expr(lhs, 0)
|
self.precedence_expr(lhs, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn precedence_expr(&mut self, lhs: Expression, min_precedence: u8) -> ParseResult<Expression> {
|
fn precedence_expr(&mut self, mut lhs: Expression, min_precedence: u8) -> ParseResult<Expression> {
|
||||||
use tokenizer::Token::*;
|
use tokenizer::Token::*;
|
||||||
|
while let Some(Operator(op)) = self.peek() {
|
||||||
|
let precedence = self.get_precedence(&op);
|
||||||
|
if precedence < min_precedence {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
self.next();
|
||||||
|
let mut rhs = try!(self.primary_expression());
|
||||||
|
while let Some(Operator(ref op)) = self.peek() {
|
||||||
|
if self.get_precedence(op) > precedence {
|
||||||
|
let new_prec = self.get_precedence(op);
|
||||||
|
rhs = try!(self.precedence_expr(rhs, new_prec));
|
||||||
|
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lhs = Expression::BinExp(op.repr, Box::new(lhs), Box::new(rhs));
|
||||||
|
}
|
||||||
|
|
||||||
Ok(lhs)
|
Ok(lhs)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -221,11 +241,11 @@ impl Parser {
|
|||||||
Some(Identifier(var)) => {
|
Some(Identifier(var)) => {
|
||||||
self.next();
|
self.next();
|
||||||
match self.peek() {
|
match self.peek() {
|
||||||
Some(LParen) => try!(self.call_expr()),
|
Some(Token::LParen) => try!(self.call_expr()),
|
||||||
_ => Expression::Variable(var)
|
_ => Expression::Variable(var)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Some(LParen) => { try!(self.paren_expr()) }
|
Some(Token::LParen) => { try!(self.paren_expr()) }
|
||||||
Some(x) => return ParseError::result_from_str("Expected primary expression"),
|
Some(x) => return ParseError::result_from_str("Expected primary expression"),
|
||||||
None => return ParseError::result_from_str("Expected primary expression received EoI")
|
None => return ParseError::result_from_str("Expected primary expression received EoI")
|
||||||
};
|
};
|
||||||
@ -238,9 +258,9 @@ impl Parser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn paren_expr(&mut self) -> ParseResult<Expression> {
|
fn paren_expr(&mut self) -> ParseResult<Expression> {
|
||||||
expect!(self, LParen, "Expected LParen");
|
expect!(self, Token::LParen, "Expected LParen");
|
||||||
let expr = try!(self.expression());
|
let expr = try!(self.expression());
|
||||||
expect!(self, RParen, "Expected LParen");
|
expect!(self, Token::RParen, "Expected LParen");
|
||||||
Ok(expr)
|
Ok(expr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -249,3 +269,28 @@ pub fn parse(tokens: &[Token], _parsed_tree: &[ASTNode]) -> ParseResult<AST> {
|
|||||||
let mut parser = Parser::initialize(tokens);
|
let mut parser = Parser::initialize(tokens);
|
||||||
parser.program()
|
parser.program()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use tokenizer;
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
macro_rules! parsetest {
|
||||||
|
($input:expr, $output:pat) => {
|
||||||
|
{
|
||||||
|
let tokens = tokenizer::tokenize($input).unwrap();
|
||||||
|
let ast = parse(&tokens, &[]).unwrap();
|
||||||
|
match &ast[..] {
|
||||||
|
$output => (),
|
||||||
|
x => panic!("Error in parse test, got {:?} instead", x)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_test() {
|
||||||
|
parsetest!("a", [ASTNode::ExprNode(Expression::Variable("a"))]);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user