diff --git a/src/bin/bin.rs b/src/bin/bin.rs index f7f5e36cc5..adb04000ba 100644 --- a/src/bin/bin.rs +++ b/src/bin/bin.rs @@ -1,10 +1,14 @@ extern crate js; use js::syntax::lexer::Lexer; +use js::syntax::parser::Parser; use std::fs::read_to_string; pub fn main() { let buffer = read_to_string("tests/js/defineVar.js").unwrap(); - let mut lexer = Lexer::new(&buffer); - lexer.lex().expect("finished"); - println!("{:?}", lexer.tokens); + let lexer = Lexer::new(&buffer); + let tokens = lexer.tokens; + match Parser::new(tokens).parse_all() { + Ok(e) => println!("{}", e), + Err(e) => println!("{:?}", e), + } } diff --git a/src/lib/syntax/ast/constant.rs b/src/lib/syntax/ast/constant.rs index f7c34fd1fb..747803d8c2 100644 --- a/src/lib/syntax/ast/constant.rs +++ b/src/lib/syntax/ast/constant.rs @@ -1,6 +1,6 @@ use std::fmt::{Display, Formatter, Result}; -#[derive(Clone, PartialEq)] +#[derive(Clone, Debug, PartialEq)] /// A Javascript Constant pub enum Const { /// A UTF-8 string, such as `"Hello, world"` diff --git a/src/lib/syntax/ast/expr.rs b/src/lib/syntax/ast/expr.rs index f2ab3f2bf3..5478457143 100644 --- a/src/lib/syntax/ast/expr.rs +++ b/src/lib/syntax/ast/expr.rs @@ -4,7 +4,7 @@ use syntax::ast::constant::Const; use syntax::ast::op::{BinOp, Operator, UnaryOp}; use syntax::ast::pos::Position; -#[derive(Clone, PartialEq)] +#[derive(Clone, Debug, PartialEq)] pub struct Expr { /// The expression definition pub def: ExprDef, @@ -31,7 +31,7 @@ impl Display for Expr { } } -#[derive(Clone, PartialEq)] +#[derive(Clone, Debug, PartialEq)] /// A Javascript Expression pub enum ExprDef { /// Run a operation between 2 expressions diff --git a/src/lib/syntax/ast/keyword.rs b/src/lib/syntax/ast/keyword.rs index 69ed5f628d..8866fa066e 100644 --- a/src/lib/syntax/ast/keyword.rs +++ b/src/lib/syntax/ast/keyword.rs @@ -6,6 +6,7 @@ use syntax::ast::keyword::Keyword::*; #[derive(Clone, PartialEq, Debug)] /// A Javascript Keyword +/// As specificed by https://www.ecma-international.org/ecma-262/#sec-keywords pub enum Keyword { /// The `break` keyword Break, diff --git a/src/lib/syntax/ast/op.rs b/src/lib/syntax/ast/op.rs index f8b318cbd2..cd05b35be5 100644 --- a/src/lib/syntax/ast/op.rs +++ b/src/lib/syntax/ast/op.rs @@ -12,7 +12,7 @@ pub trait Operator { } } -#[derive(Clone, PartialEq)] +#[derive(Clone, Debug, PartialEq)] /// A numeric operation between 2 values pub enum NumOp { /// `a + b` - Addition @@ -43,7 +43,7 @@ impl Display for NumOp { } } -#[derive(Clone, PartialEq)] +#[derive(Clone, Debug, PartialEq)] /// A unary operation on a single value pub enum UnaryOp { /// `a++` - increment the value @@ -78,7 +78,7 @@ impl Display for UnaryOp { } } -#[derive(Clone, PartialEq)] +#[derive(Clone, Debug, PartialEq)] /// A bitwise operation between 2 values pub enum BitOp { /// `a & b` - Bitwise and @@ -109,7 +109,7 @@ impl Display for BitOp { } } -#[derive(Clone, PartialEq)] +#[derive(Clone, Debug, PartialEq)] /// A comparitive operation between 2 values pub enum CompOp { /// `a == b` - Equality @@ -149,7 +149,7 @@ impl Display for CompOp { } } -#[derive(Clone, PartialEq)] +#[derive(Clone, Debug, PartialEq)] /// A logical operation between 2 boolean values pub enum LogOp { /// `a && b` - Logical and @@ -171,7 +171,7 @@ impl Display for LogOp { } } -#[derive(Clone, PartialEq)] +#[derive(Clone, Debug, PartialEq)] /// A binary operation between 2 values pub enum BinOp { /// Numeric operation diff --git a/src/lib/syntax/parser.rs b/src/lib/syntax/parser.rs index a91ced0876..787e2f8d05 100644 --- a/src/lib/syntax/parser.rs +++ b/src/lib/syntax/parser.rs @@ -2,20 +2,21 @@ use std::collections::btree_map::BTreeMap; use syntax::ast::constant::Const; use syntax::ast::expr::{Expr, ExprDef}; use syntax::ast::keyword::Keyword; -use syntax::ast::op::UnaryOp; +use syntax::ast::op::{BinOp, BitOp, CompOp, LogOp, NumOp, Operator, UnaryOp}; use syntax::ast::punc::Punctuator; use syntax::ast::token::{Token, TokenData}; macro_rules! mk ( ($this:expr, $def:expr) => ( - Expr::new($def, try!($this.get_token($this.pos - 1)).pos, try!($this.get_token($this.pos - 1)).pos) + Expr::new($def, try!($this.get_token($this.pos)).pos, try!($this.get_token($this.pos)).pos) ); ($this:expr, $def:expr, $first:expr) => ( - Expr::new($def, $first.pos, try!($this.get_token($this.pos - 1)).pos) + Expr::new($def, $first.pos, try!($this.get_token($this.pos)).pos) ); ); -/// An error encounted during parsing an expression +/// ParseError is an enum which represents errors encounted during parsing an expression +#[derive(Debug, Clone)] pub enum ParseError { /// When it expected a certain kind of token, but got another as part of something Expected(Vec, Token, &'static str), @@ -52,6 +53,7 @@ impl Parser { let result = try!(self.parse()); exprs.push(result); } + println!("{}", exprs[0]); Ok(mk!(self, ExprDef::BlockExpr(exprs))) } @@ -63,6 +65,223 @@ impl Parser { } } + fn parse_struct(&mut self, keyword: Keyword) -> ParseResult { + match keyword { + Keyword::Throw => { + let thrown = try!(self.parse()); + Ok(mk!(self, ExprDef::ThrowExpr(Box::new(thrown)))) + } + Keyword::Var => { + let mut vars = Vec::new(); + loop { + let name = match self.get_token(self.pos) { + Ok(Token { + data: TokenData::Identifier(ref name), + .. + }) => name.clone(), + Ok(tok) => { + return Err(ParseError::Expected( + vec![TokenData::Identifier("identifier".to_string())], + tok, + "var statement", + )) + } + Err(ParseError::AbruptEnd) => break, + Err(e) => return Err(e), + }; + self.pos += 1; + match self.get_token(self.pos) { + Ok(Token { + data: TokenData::Punctuator(Punctuator::Assign), + .. + }) => { + self.pos += 1; + let val = self.parse()?; + vars.push((name, Some(val))); + match self.get_token(self.pos) { + Ok(Token { + data: TokenData::Punctuator(Punctuator::Comma), + .. + }) => self.pos += 1, + _ => break, + } + } + Ok(Token { + data: TokenData::Punctuator(Punctuator::Comma), + .. + }) => { + self.pos += 1; + vars.push((name, None)); + } + _ => { + vars.push((name, None)); + break; + } + } + } + Ok(mk!(self, ExprDef::VarDeclExpr(vars))) + } + Keyword::Return => Ok(mk!( + self, + ExprDef::ReturnExpr(Some(Box::new(self.parse()?.clone()))) + )), + Keyword::New => { + let call = self.parse()?; + match call.def { + ExprDef::CallExpr(ref func, ref args) => Ok(mk!( + self, + ExprDef::ConstructExpr(func.clone(), args.clone()) + )), + _ => Err(ParseError::ExpectedExpr("constructor", call)), + } + } + Keyword::TypeOf => Ok(mk!(self, ExprDef::TypeOfExpr(Box::new(self.parse()?)))), + Keyword::If => { + self.expect_punc(Punctuator::OpenParen, "if block")?; + let cond = self.parse()?; + self.expect_punc(Punctuator::CloseParen, "if block")?; + let expr = self.parse()?; + let next = self.get_token(self.pos + 1); + Ok(mk!( + self, + ExprDef::IfExpr( + Box::new(cond), + Box::new(expr), + if next.is_ok() && next.unwrap().data == TokenData::Keyword(Keyword::Else) { + self.pos += 2; + Some(Box::new(self.parse()?)) + } else { + None + } + ) + )) + } + Keyword::While => { + self.expect_punc(Punctuator::OpenParen, "while condition")?; + let cond = self.parse()?; + self.expect_punc(Punctuator::CloseParen, "while condition")?; + let expr = self.parse()?; + Ok(mk!( + self, + ExprDef::WhileLoopExpr(Box::new(cond), Box::new(expr)) + )) + } + Keyword::Switch => { + try!(self.expect_punc(Punctuator::OpenParen, "switch value")); + let value = self.parse(); + try!(self.expect_punc(Punctuator::CloseParen, "switch value")); + try!(self.expect_punc(Punctuator::OpenBlock, "switch block")); + let mut cases = Vec::new(); + let mut default = None; + while self.pos + 1 < self.tokens.len() { + let tok = self.get_token(self.pos)?; + self.pos += 1; + match tok.data { + TokenData::Keyword(Keyword::Case) => { + let cond = self.parse(); + let mut block = Vec::new(); + try!(self.expect_punc(Punctuator::Colon, "switch case")); + loop { + match try!(self.get_token(self.pos)).data { + TokenData::Keyword(Keyword::Case) + | TokenData::Keyword(Keyword::Default) => break, + TokenData::Punctuator(Punctuator::CloseBlock) => break, + _ => block.push(try!(self.parse())), + } + } + cases.push((cond.unwrap(), block)); + } + TokenData::Keyword(Keyword::Default) => { + let mut block = Vec::new(); + try!(self.expect_punc(Punctuator::Colon, "default switch case")); + loop { + match try!(self.get_token(self.pos)).data { + TokenData::Keyword(Keyword::Case) + | TokenData::Keyword(Keyword::Default) => break, + TokenData::Punctuator(Punctuator::CloseBlock) => break, + _ => block.push(try!(self.parse())), + } + } + default = Some(mk!(self, ExprDef::BlockExpr(block))); + } + TokenData::Punctuator(Punctuator::CloseBlock) => break, + _ => { + return Err(ParseError::Expected( + vec![ + TokenData::Keyword(Keyword::Case), + TokenData::Keyword(Keyword::Default), + TokenData::Punctuator(Punctuator::CloseBlock), + ], + tok, + "switch block", + )) + } + } + } + try!(self.expect_punc(Punctuator::CloseBlock, "switch block")); + Ok(mk!( + self, + ExprDef::SwitchExpr( + Box::new(value.unwrap()), + cases, + match default { + Some(v) => Some(Box::new(v)), + None => None, + } + ) + )) + } + Keyword::Function => { + // function [identifier] () { etc } + let tk = try!(self.get_token(self.pos)); + let name = match tk.data { + TokenData::Identifier(ref name) => { + self.pos += 1; + Some(name.clone()) + } + TokenData::Punctuator(Punctuator::OpenParen) => None, + _ => { + return Err(ParseError::Expected( + vec![TokenData::Identifier("identifier".to_string())], + tk.clone(), + "function name", + )) + } + }; + // Now we have the function identifier we should have an open paren for arguments ( ) + self.expect_punc(Punctuator::OpenParen, "function")?; + let mut args: Vec = Vec::new(); + let mut tk = self.get_token(self.pos)?; + while tk.data != TokenData::Punctuator(Punctuator::CloseParen) { + match tk.data { + TokenData::Identifier(ref id) => args.push(id.clone()), + _ => { + return Err(ParseError::Expected( + vec![TokenData::Identifier("identifier".to_string())], + tk.clone(), + "function arguments", + )) + } + } + self.pos += 1; + if try!(self.get_token(self.pos)).data + == TokenData::Punctuator(Punctuator::Comma) + { + self.pos += 1; + } + tk = self.get_token(self.pos)?; + } + self.pos += 1; + let block = self.parse()?; + Ok(mk!( + self, + ExprDef::FunctionDeclExpr(name, args, Box::new(block)) + )) + } + _ => Err(ParseError::UnexpectedKeyword(keyword)), + } + } + /// Parse a single expression pub fn parse(&mut self) -> ParseResult { if self.pos > self.tokens.len() { @@ -80,7 +299,7 @@ impl Parser { mk!(self, ExprDef::ConstExpr(Const::Undefined)) } TokenData::NumericLiteral(num) => mk!(self, ExprDef::ConstExpr(Const::Num(num))), - TNullLiteral => mk!(self, ExprDef::ConstExpr(Const::Null)), + TokenData::NullLiteral => mk!(self, ExprDef::ConstExpr(Const::Null)), TokenData::StringLiteral(text) => mk!(self, ExprDef::ConstExpr(Const::String(text))), TokenData::BooleanLiteral(val) => mk!(self, ExprDef::ConstExpr(Const::Bool(val))), TokenData::Identifier(ref s) if s == "undefined" => { @@ -88,7 +307,7 @@ impl Parser { } TokenData::Identifier(s) => mk!(self, ExprDef::LocalExpr(s)), TokenData::Keyword(keyword) => try!(self.parse_struct(keyword)), - TokenData::Punctuator(POpenParen) => { + TokenData::Punctuator(Punctuator::OpenParen) => { match try!(self.get_token(self.pos)).data { TokenData::Punctuator(Punctuator::CloseParen) if try!(self.get_token(self.pos + 1)).data @@ -141,7 +360,7 @@ impl Parser { vec![TokenData::Identifier( "identifier".to_string(), )], - curr_tk, + curr_tk.clone(), "arrow function", )) } @@ -179,7 +398,7 @@ impl Parser { } } } - TokenData::Punctuator(POpenBracket) => { + TokenData::Punctuator(Punctuator::OpenBracket) => { let mut array: Vec = Vec::new(); let mut expect_comma_or_end = try!(self.get_token(self.pos)).data == TokenData::Punctuator(Punctuator::CloseBracket); @@ -243,12 +462,14 @@ impl Parser { TokenData::Identifier(ref id) => id.clone(), TokenData::StringLiteral(ref str) => str.clone(), _ => { - return Err(vec![ - TokenData::Identifier("identifier".to_string()), - TokenData::StringLiteral("string".to_string()), + return Err(ParseError::Expected( + vec![ + TokenData::Identifier("identifier".to_string()), + TokenData::StringLiteral("string".to_string()), + ], tk, "object declaration", - ]) + )) } }; self.pos += 1; @@ -276,23 +497,23 @@ impl Parser { self.pos += 1; mk!(self, ExprDef::BlockExpr(exprs), token) } - TokenData::Punctuator(PSub) => mk!( + TokenData::Punctuator(Punctuator::Sub) => mk!( self, ExprDef::UnaryOpExpr(UnaryOp::Minus, Box::new(try!(self.parse()))) ), - TokenData::Punctuator(PAdd) => mk!( + TokenData::Punctuator(Punctuator::Add) => mk!( self, ExprDef::UnaryOpExpr(UnaryOp::Plus, Box::new(try!(self.parse()))) ), - TokenData::Punctuator(PNot) => mk!( + TokenData::Punctuator(Punctuator::Not) => mk!( self, ExprDef::UnaryOpExpr(UnaryOp::Not, Box::new(try!(self.parse()))) ), - TokenData::Punctuator(PInc) => mk!( + TokenData::Punctuator(Punctuator::Inc) => mk!( self, ExprDef::UnaryOpExpr(UnaryOp::IncrementPre, Box::new(try!(self.parse()))) ), - TokenData::Punctuator(PDec) => mk!( + TokenData::Punctuator(Punctuator::Dec) => mk!( self, ExprDef::UnaryOpExpr(UnaryOp::DecrementPre, Box::new(try!(self.parse()))) ), @@ -304,4 +525,228 @@ impl Parser { self.parse_next(expr) } } + + fn parse_next(&mut self, expr: Expr) -> ParseResult { + let next = self.get_token(self.pos)?; + let mut carry_on = true; + let mut result = expr.clone(); + match next.data { + TokenData::Punctuator(Punctuator::Dot) => { + self.pos += 1; + let tk = try!(self.get_token(self.pos)); + match tk.data { + TokenData::Identifier(ref s) => { + result = mk!( + self, + ExprDef::GetConstFieldExpr(Box::new(expr), s.to_string()) + ) + } + _ => { + return Err(ParseError::Expected( + vec![TokenData::Identifier("identifier".to_string())], + tk, + "field access", + )) + } + } + self.pos += 1; + } + TokenData::Punctuator(Punctuator::OpenParen) => { + let mut args = Vec::new(); + let mut expect_comma_or_end = try!(self.get_token(self.pos + 1)).data + == TokenData::Punctuator(Punctuator::CloseParen); + loop { + self.pos += 1; + let token = try!(self.get_token(self.pos)); + if token.data == TokenData::Punctuator(Punctuator::CloseParen) + && expect_comma_or_end + { + self.pos += 1; + break; + } else if token.data == TokenData::Punctuator(Punctuator::Comma) + && expect_comma_or_end + { + expect_comma_or_end = false; + } else if expect_comma_or_end { + return Err(ParseError::Expected( + vec![ + TokenData::Punctuator(Punctuator::Comma), + TokenData::Punctuator(Punctuator::CloseParen), + ], + token, + "function call arguments", + )); + } else { + let parsed = try!(self.parse()); + self.pos -= 1; + args.push(parsed); + expect_comma_or_end = true; + } + } + result = mk!(self, ExprDef::CallExpr(Box::new(expr), args)); + } + TokenData::Punctuator(Punctuator::Question) => { + self.pos += 1; + let if_e = try!(self.parse()); + try!(self.expect(TokenData::Punctuator(Punctuator::Colon), "if expression")); + let else_e = try!(self.parse()); + result = mk!( + self, + ExprDef::IfExpr(Box::new(expr), Box::new(if_e), Some(Box::new(else_e))) + ); + } + TokenData::Punctuator(Punctuator::OpenBracket) => { + self.pos += 1; + let index = try!(self.parse()); + try!(self.expect( + TokenData::Punctuator(Punctuator::CloseBracket), + "array index" + )); + result = mk!(self, ExprDef::GetFieldExpr(Box::new(expr), Box::new(index))); + } + TokenData::Punctuator(Punctuator::Semicolon) | TokenData::Comment(_) => { + self.pos += 1; + } + TokenData::Punctuator(Punctuator::Assign) => { + self.pos += 1; + let next = try!(self.parse()); + result = mk!(self, ExprDef::AssignExpr(Box::new(expr), Box::new(next))); + } + TokenData::Punctuator(Punctuator::Arrow) => { + self.pos += 1; + let mut args = Vec::with_capacity(1); + match result.def { + ExprDef::LocalExpr(name) => args.push(name), + _ => return Err(ParseError::ExpectedExpr("identifier", result)), + } + let next = try!(self.parse()); + result = mk!(self, ExprDef::ArrowFunctionDeclExpr(args, Box::new(next))); + } + TokenData::Punctuator(Punctuator::Add) => { + result = try!(self.binop(BinOp::Num(NumOp::Add), expr)) + } + TokenData::Punctuator(Punctuator::Sub) => { + result = try!(self.binop(BinOp::Num(NumOp::Sub), expr)) + } + TokenData::Punctuator(Punctuator::Mul) => { + result = try!(self.binop(BinOp::Num(NumOp::Mul), expr)) + } + TokenData::Punctuator(Punctuator::Div) => { + result = try!(self.binop(BinOp::Num(NumOp::Div), expr)) + } + TokenData::Punctuator(Punctuator::Mod) => { + result = try!(self.binop(BinOp::Num(NumOp::Mod), expr)) + } + TokenData::Punctuator(Punctuator::BoolAnd) => { + result = try!(self.binop(BinOp::Log(LogOp::And), expr)) + } + TokenData::Punctuator(Punctuator::BoolOr) => { + result = try!(self.binop(BinOp::Log(LogOp::Or), expr)) + } + TokenData::Punctuator(Punctuator::And) => { + result = try!(self.binop(BinOp::Bit(BitOp::And), expr)) + } + TokenData::Punctuator(Punctuator::Or) => { + result = try!(self.binop(BinOp::Bit(BitOp::Or), expr)) + } + TokenData::Punctuator(Punctuator::Xor) => { + result = try!(self.binop(BinOp::Bit(BitOp::Xor), expr)) + } + TokenData::Punctuator(Punctuator::LeftSh) => { + result = try!(self.binop(BinOp::Bit(BitOp::Shl), expr)) + } + TokenData::Punctuator(Punctuator::RightSh) => { + result = try!(self.binop(BinOp::Bit(BitOp::Shr), expr)) + } + TokenData::Punctuator(Punctuator::Eq) => { + result = try!(self.binop(BinOp::Comp(CompOp::Equal), expr)) + } + TokenData::Punctuator(Punctuator::NotEq) => { + result = try!(self.binop(BinOp::Comp(CompOp::NotEqual), expr)) + } + TokenData::Punctuator(Punctuator::StrictEq) => { + result = try!(self.binop(BinOp::Comp(CompOp::StrictEqual), expr)) + } + TokenData::Punctuator(Punctuator::StrictNotEq) => { + result = try!(self.binop(BinOp::Comp(CompOp::StrictNotEqual), expr)) + } + TokenData::Punctuator(Punctuator::LessThan) => { + result = try!(self.binop(BinOp::Comp(CompOp::LessThan), expr)) + } + TokenData::Punctuator(Punctuator::LessThanOrEq) => { + result = try!(self.binop(BinOp::Comp(CompOp::LessThanOrEqual), expr)) + } + TokenData::Punctuator(Punctuator::GreaterThan) => { + result = try!(self.binop(BinOp::Comp(CompOp::GreaterThan), expr)) + } + TokenData::Punctuator(Punctuator::GreaterThanOrEq) => { + result = try!(self.binop(BinOp::Comp(CompOp::GreaterThanOrEqual), expr)) + } + TokenData::Punctuator(Punctuator::Inc) => { + result = mk!( + self, + ExprDef::UnaryOpExpr(UnaryOp::IncrementPost, Box::new(try!(self.parse()))) + ) + } + TokenData::Punctuator(Punctuator::Dec) => { + result = mk!( + self, + ExprDef::UnaryOpExpr(UnaryOp::DecrementPost, Box::new(try!(self.parse()))) + ) + } + _ => carry_on = false, + }; + if carry_on && self.pos < self.tokens.len() { + self.parse_next(result) + } else { + Ok(result) + } + } + + fn binop(&mut self, op: BinOp, orig: Expr) -> Result { + let (precedence, assoc) = op.get_precedence_and_assoc(); + self.pos += 1; + let next = try!(self.parse()); + Ok(match next.def { + ExprDef::BinOpExpr(ref op2, ref a, ref b) => { + let other_precedence = op2.get_precedence(); + if precedence < other_precedence || (precedence == other_precedence && !assoc) { + mk!( + self, + ExprDef::BinOpExpr( + op2.clone(), + b.clone(), + Box::new(mk!( + self, + ExprDef::BinOpExpr(op.clone(), Box::new(orig), a.clone()) + )) + ) + ) + } else { + mk!( + self, + ExprDef::BinOpExpr(op, Box::new(orig), Box::new(next.clone())) + ) + } + } + _ => mk!(self, ExprDef::BinOpExpr(op, Box::new(orig), Box::new(next))), + }) + } + + /// Returns an error if the next symbol is not `tk` + fn expect(&mut self, tk: TokenData, routine: &'static str) -> Result<(), ParseError> { + self.pos += 1; + let curr_tk = self.get_token(self.pos - 1)?; + if curr_tk.data != tk { + Err(ParseError::Expected(vec![tk], curr_tk, routine)) + } else { + Ok(()) + } + } + + /// Returns an error if the next symbol is not the punctuator `p` + #[inline(always)] + fn expect_punc(&mut self, p: Punctuator, routine: &'static str) -> Result<(), ParseError> { + self.expect(TokenData::Punctuator(p), routine) + } } diff --git a/tests/js/defineVar.js b/tests/js/defineVar.js index 7df9857fb8..bba55c721b 100644 --- a/tests/js/defineVar.js +++ b/tests/js/defineVar.js @@ -1 +1 @@ -let a = new String("Hello World"); \ No newline at end of file +var a \ No newline at end of file