// This grammar has been adapted from // https://craftinginterpreters.com/appendix-i.html#syntax-grammar. use lalrpop_util::ParseError; use crate::error::ErrorS; use crate::syntax::{ast, lexer}; grammar<'err>( errors: &'err mut Vec> ); pub Program: ast::Program = => ast::Program { <> }; // Declarations DeclS = Spanned; Decl = { DeclClass, DeclFun, DeclVar, Stmt, } DeclClass: ast::Stmt = "class" >)?> "{" *> "}" => ast::Stmt::Class(ast::StmtClass { <> }); DeclFun: ast::Stmt = "fun" => ast::Stmt::Fun(<>); DeclVar: ast::Stmt = "var" )?> ";" => ast::Stmt::Var(ast::StmtVar { var: ast::Var { name, depth: None }, value, }); // Statements // https://en.wikipedia.org/wiki/Dangling_else#Avoiding_the_conflict_in_LR_parsers Stmt: ast::Stmt = { StmtOpen, StmtClosed, => { errors.push(e.error); ast::Stmt::Error }, } StmtOpen: ast::Stmt = { "if" "(" ")" > => ast::Stmt::If(Box::new(ast::StmtIf { <>, else_: None })), "if" "(" ")" > "else" > => ast::Stmt::If(Box::new(ast::StmtIf { cond, then, else_: Some(else_) })), "while" "(" ")" > => ast::Stmt::While(Box::new(ast::StmtWhile { <> })), "for" "(" ")" > => ast::Stmt::For(Box::new(ast::StmtFor { <> })), } StmtClosed: ast::Stmt = { "if" "(" ")" > "else" > => ast::Stmt::If(Box::new(ast::StmtIf { cond, then, else_: Some(else_) })), "while" "(" ")" > => ast::Stmt::While(Box::new(ast::StmtWhile { <> })), "for" "(" ")" > => ast::Stmt::For(Box::new(ast::StmtFor { <> })), StmtSimple, } ForInit: Option = { > => Some(<>), > => Some(<>), ";" => None, } ForCond = ";"; ForIncr = ; StmtSimple = { StmtBlock, StmtExpr, StmtPrint, StmtReturn, } StmtBlock: ast::Stmt = StmtBlockInternal => ast::Stmt::Block(<>); StmtBlockInternal: ast::StmtBlock = "{" "}" => ast::StmtBlock { <> }; StmtExpr: ast::Stmt = ";" => ast::Stmt::Expr(ast::StmtExpr { <> }); StmtPrint: ast::Stmt = "print" ";" => ast::Stmt::Print(ast::StmtPrint { <> }); StmtReturn: ast::Stmt = "return" ";" => ast::Stmt::Return(ast::StmtReturn { <> }); // Expressions ExprS = Spanned; Expr = ExprAssign; ExprAssign = { "=" => ast::Expr::Assign(Box::new(ast::ExprAssign { var: ast::Var { name, depth: None }, value, })), > "." "=" => ast::Expr::Set(Box::new(ast::ExprSet { <> })), ExprLogicOr, } ExprLogicOr = ExprInfix; OpLogicOr: ast::OpInfix = "or" => ast::OpInfix::LogicOr; ExprLogicAnd = ExprInfix; OpLogicAnd: ast::OpInfix = "and" => ast::OpInfix::LogicAnd; ExprEquality = ExprInfix; OpEquality: ast::OpInfix = { "==" => ast::OpInfix::Equal, "!=" => ast::OpInfix::NotEqual, } ExprComparison = ExprInfix; OpComparison: ast::OpInfix = { ">" => ast::OpInfix::Greater, ">=" => ast::OpInfix::GreaterEqual, "<" => ast::OpInfix::Less, "<=" => ast::OpInfix::LessEqual, } ExprTerm = ExprInfix; OpTerm: ast::OpInfix = { "+" => ast::OpInfix::Add, "-" => ast::OpInfix::Subtract, } ExprFactor = ExprInfix; OpFactor: ast::OpInfix = { "*" => ast::OpInfix::Multiply, "/" => ast::OpInfix::Divide, } ExprPrefix: ast::Expr = { > => ast::Expr::Prefix(Box::new(ast::ExprPrefix { <> })), ExprCall, } OpPrefix: ast::OpPrefix = { "-" => ast::OpPrefix::Negate, "!" => ast::OpPrefix::Not, } ExprCall: ast::Expr = { > "(" ")" => ast::Expr::Call(Box::new(ast::ExprCall { callee, args })), > "." => ast::Expr::Get(Box::new(ast::ExprGet { <> })), "super" "." => ast::Expr::Super(ast::ExprSuper { super_: ast::Var { name: "super".to_string(), depth: None, }, name, }), ExprPrimary, } ExprPrimary: ast::Expr = { // Literals "nil" => ast::Expr::Literal(ast::ExprLiteral::Nil), "false" => ast::Expr::Literal(ast::ExprLiteral::Bool(false)), "true" => ast::Expr::Literal(ast::ExprLiteral::Bool(true)), string => ast::Expr::Literal(ast::ExprLiteral::String(<>)), number => ast::Expr::Literal(ast::ExprLiteral::Number(<>)), // Variables ExprVar, ExprThis, // Grouping "(" ")", } ExprVar: ast::Expr = => ast::Expr::Var(ast::ExprVar { var: ast::Var { name, depth: None } }); ExprThis: ast::Expr = "this" => ast::Expr::Var(ast::ExprVar { var: ast::Var { name: "this".to_string(), depth: None, }}); // Utilities Spanned: ast::Spanned = => (t, l..r); Function: ast::StmtFun = "(" ")" => ast::StmtFun { <> }; Params: Vec = { )*> => { params.insert(0, first); params }, () => Vec::new(), }; Args: Vec = { )*> => { args.insert(0, first); args }, () => Vec::new(), } ExprInfix: ast::Expr = { > > => ast::Expr::Infix(Box::new(ast::ExprInfix { <> })), Rt, } extern { type Location = usize; type Error = ErrorS; enum lexer::Token { // Single-character tokens. "(" => lexer::Token::LtParen, ")" => lexer::Token::RtParen, "{" => lexer::Token::LtBrace, "}" => lexer::Token::RtBrace, "," => lexer::Token::Comma, "." => lexer::Token::Dot, "-" => lexer::Token::Minus, "+" => lexer::Token::Plus, ";" => lexer::Token::Semicolon, "/" => lexer::Token::Slash, "*" => lexer::Token::Asterisk, // One or two character tokens. "!" => lexer::Token::Bang, "!=" => lexer::Token::BangEqual, "=" => lexer::Token::Equal, "==" => lexer::Token::EqualEqual, ">" => lexer::Token::Greater, ">=" => lexer::Token::GreaterEqual, "<" => lexer::Token::Less, "<=" => lexer::Token::LessEqual, // Literals. identifier => lexer::Token::Identifier(), string => lexer::Token::String(), number => lexer::Token::Number(), // Keywords. "and" => lexer::Token::And, "class" => lexer::Token::Class, "else" => lexer::Token::Else, "false" => lexer::Token::False, "for" => lexer::Token::For, "fun" => lexer::Token::Fun, "if" => lexer::Token::If, "nil" => lexer::Token::Nil, "or" => lexer::Token::Or, "print" => lexer::Token::Print, "return" => lexer::Token::Return, "super" => lexer::Token::Super, "this" => lexer::Token::This, "true" => lexer::Token::True, "var" => lexer::Token::Var, "while" => lexer::Token::While, } }