Browse Source

parser finished but runtime panics

pull/1/head
Jason Williams 6 years ago
parent
commit
cdf992a9ac
  1. 10
      src/bin/bin.rs
  2. 2
      src/lib/syntax/ast/constant.rs
  3. 4
      src/lib/syntax/ast/expr.rs
  4. 1
      src/lib/syntax/ast/keyword.rs
  5. 12
      src/lib/syntax/ast/op.rs
  6. 475
      src/lib/syntax/parser.rs
  7. 2
      tests/js/defineVar.js

10
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),
}
}

2
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"`

4
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

1
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,

12
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

475
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<TokenData>, 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<String> = 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<Expr> = 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![
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<Expr, ParseError> {
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)
}
}

2
tests/js/defineVar.js

@ -1 +1 @@
let a = new String("Hello World");
var a
Loading…
Cancel
Save