diff --git a/src/lib/exec.rs b/src/lib/exec.rs index 84a773aa63..f0b2372a23 100644 --- a/src/lib/exec.rs +++ b/src/lib/exec.rs @@ -368,14 +368,10 @@ impl Executor for Interpreter { Ok(Gc::new(ValueData::Undefined)) } ExprDef::ConstDeclExpr(ref vars) => { - for var in vars.iter() { - let (name, value) = var.clone(); - let val = match value { - Some(v) => self.run(&v)?, - None => Gc::new(ValueData::Null), - }; + for (name, value) in vars.iter() { self.environment .create_immutable_binding(name.clone(), false); + let val = self.run(&value)?; self.environment.initialize_binding(&name, val); } Ok(Gc::new(ValueData::Undefined)) diff --git a/src/lib/syntax/ast/expr.rs b/src/lib/syntax/ast/expr.rs index 9dabe12878..b9c7962165 100644 --- a/src/lib/syntax/ast/expr.rs +++ b/src/lib/syntax/ast/expr.rs @@ -36,7 +36,7 @@ pub enum ExprDef { /// Make a constant value ConstExpr(Const), /// Const declaration - ConstDeclExpr(Vec<(String, Option)>), + ConstDeclExpr(Vec<(String, Expr)>), /// Construct an object from the function and arg{ ConstructExpr(Box, Vec), /// Run several expressions from top-to-bottom @@ -192,17 +192,26 @@ impl Display for ExprDef { ExprDef::ReturnExpr(None) => write!(f, "return"), ExprDef::ThrowExpr(ref ex) => write!(f, "throw {}", ex), ExprDef::AssignExpr(ref ref_e, ref val) => write!(f, "{} = {}", ref_e, val), - ExprDef::VarDeclExpr(ref vars) - | ExprDef::LetDeclExpr(ref vars) - | ExprDef::ConstDeclExpr(ref vars) => { - f.write_str("var ")?; + ExprDef::VarDeclExpr(ref vars) | ExprDef::LetDeclExpr(ref vars) => { + if let ExprDef::VarDeclExpr(_) = *self { + f.write_str("var ")?; + } else { + f.write_str("let ")?; + } for (key, val) in vars.iter() { match val { Some(x) => f.write_fmt(format_args!("{} = {}", key, x))?, None => f.write_fmt(format_args!("{}", key))?, } } - f.write_str("") + Ok(()) + } + ExprDef::ConstDeclExpr(ref vars) => { + f.write_str("const ")?; + for (key, val) in vars.iter() { + f.write_fmt(format_args!("{} = {}", key, val))? + } + Ok(()) } ExprDef::TypeOfExpr(ref e) => write!(f, "typeof {}", e), } diff --git a/src/lib/syntax/parser.rs b/src/lib/syntax/parser.rs index 0d23f99fd4..bf23b27565 100644 --- a/src/lib/syntax/parser.rs +++ b/src/lib/syntax/parser.rs @@ -74,7 +74,7 @@ impl Parser { Ok(mk!(self, ExprDef::ThrowExpr(Box::new(thrown)))) } // vars, lets and consts are similar in parsing structure, we can group them together - Keyword::Var | Keyword::Let | Keyword::Const => { + Keyword::Var | Keyword::Let => { let mut vars = Vec::new(); loop { let name = match self.get_token(self.pos) { @@ -86,7 +86,7 @@ impl Parser { return Err(ParseError::Expected( vec![TokenData::Identifier("identifier".to_string())], tok, - "var statement", + "var/let declaration", )) } Err(ParseError::AbruptEnd) => break, @@ -125,10 +125,57 @@ impl Parser { match keyword { Keyword::Let => Ok(Expr::new(ExprDef::LetDeclExpr(vars))), - Keyword::Const => Ok(Expr::new(ExprDef::ConstDeclExpr(vars))), _ => Ok(Expr::new(ExprDef::VarDeclExpr(vars))), } } + Keyword::Const => { + 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, + "const declaration", + )) + } + 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, val)); + match self.get_token(self.pos) { + Ok(Token { + data: TokenData::Punctuator(Punctuator::Comma), + .. + }) => self.pos += 1, + _ => break, + } + } + Ok(tok) => { + return Err(ParseError::Expected( + vec![TokenData::Punctuator(Punctuator::Assign)], + tok, + "const declration", + )) + } + _ => break, + } + } + + Ok(Expr::new(ExprDef::ConstDeclExpr(vars))) + } Keyword::Return => Ok(mk!( self, ExprDef::ReturnExpr(Some(Box::new(self.parse()?.clone()))) @@ -947,7 +994,7 @@ mod tests { "const a = 5;", &[Expr::new(ExprDef::ConstDeclExpr(vec![( String::from("a"), - Some(Expr::new(ExprDef::ConstExpr(Const::Num(5.0)))), + Expr::new(ExprDef::ConstExpr(Const::Num(5.0))), )]))], ); @@ -956,27 +1003,24 @@ mod tests { "const a=5;", &[Expr::new(ExprDef::ConstDeclExpr(vec![( String::from("a"), - Some(Expr::new(ExprDef::ConstExpr(Const::Num(5.0)))), + Expr::new(ExprDef::ConstExpr(Const::Num(5.0))), )]))], ); // Check empty `const` declaration - // FIXME: This does not work, it should fail to parse an unitialized const declaration: - // - // check_invalid("const a;"); + check_invalid("const a;"); // Check multiple `const` declaration check_parser( - "const a = 5, b, c = 6;", + "const a = 5, c = 6;", &[Expr::new(ExprDef::ConstDeclExpr(vec![ ( String::from("a"), - Some(Expr::new(ExprDef::ConstExpr(Const::Num(5.0)))), + Expr::new(ExprDef::ConstExpr(Const::Num(5.0))), ), - (String::from("b"), None), ( String::from("c"), - Some(Expr::new(ExprDef::ConstExpr(Const::Num(6.0)))), + Expr::new(ExprDef::ConstExpr(Const::Num(6.0))), ), ]))], );