%start Body %left '||' %left '&&' %left '<' '>' '<=' '>=' '==' '!=' %left '*' '/' %left '+' '-' %% Body -> Result>: Body 'EOL' Stmt { flatten($1, $3) } | Body 'EOL' { $1 } | Stmt { Ok(vec![$1?]) } | { Ok(Vec::new()) } ; Stmt -> Result: Expr { Ok(Stmt::Expr($1?)) } //| Assign { todo!() } ; Expr -> Result: BinExpr { $1 }; BinExpr -> Result: UnExpr '||' BinExpr { Ok(BinExpr::new_expr($1?, BinOp::Or, $3?)) } | UnExpr '&&' BinExpr { Ok(BinExpr::new_expr($1?, BinOp::And, $3?)) } | UnExpr '<' BinExpr { Ok(BinExpr::new_expr($1?, BinOp::Lt, $3?)) } | UnExpr '>' BinExpr { Ok(BinExpr::new_expr($1?, BinOp::Gt, $3?)) } | UnExpr '<=' BinExpr { Ok(BinExpr::new_expr($1?, BinOp::Le, $3?)) } | UnExpr '>=' BinExpr { Ok(BinExpr::new_expr($1?, BinOp::Ge, $3?)) } | UnExpr '==' BinExpr { Ok(BinExpr::new_expr($1?, BinOp::Eq, $3?)) } | UnExpr '!=' BinExpr { Ok(BinExpr::new_expr($1?, BinOp::Neq, $3?)) } | UnExpr '*' BinExpr { Ok(BinExpr::new_expr($1?, BinOp::Times, $3?)) } | UnExpr '/' BinExpr { Ok(BinExpr::new_expr($1?, BinOp::Div, $3?)) } | UnExpr '+' BinExpr { Ok(BinExpr::new_expr($1?, BinOp::Plus, $3?)) } | UnExpr '-' BinExpr { Ok(BinExpr::new_expr($1?, BinOp::Plus, $3?)) } | UnExpr { $1 } ; UnExpr -> Result: '+' UnExpr { Ok(UnExpr::new_expr(UnOp::Plus, $2?)) } | '-' UnExpr { Ok(UnExpr::new_expr(UnOp::Minus, $2?)) } | CallIndexExpr { $1 } ; CallIndexExpr -> Result: IndexExpr { $1 } | CallExpr { $1 } | AccessExpr { $1 } ; CallExpr -> Result: CallIndexExpr '(' FunArgs ')' { Ok(CallExpr::new_expr($1?, $3?)) } ; FunArgs -> Result>: FunArgsTail { $1 } | { Ok(Vec::new()) } ; FunArgsTail -> Result>: FunArgsTail ',' Expr { flatten($1, $3) } | Expr { Ok(vec![$1?]) } ; IndexExpr -> Result: CallIndexExpr '[' Expr ']' { Ok(IndexExpr::new_expr($1?, $3?)) } ; AccessExpr -> Result: AtomExpr { $1 } | CallIndexExpr '.' Ident { Ok(AccessExpr::new_expr($1?, $3?)) } ; AtomExpr -> Result: Atom { $1.map(Expr::Atom) } | '(' Expr ')' { $2 } ; Atom -> Result: Ident { Ok(Atom::Ident($1?)) } | 'SYM' { let v = $1.map_err(|_| ())?; let sym = &$lexer.span_str(v.span())[1..]; Ok(Atom::Sym(sym.to_string())) } | 'NUM' { let v = $1.map_err(|_| ())?; let num_text = &$lexer.span_str(v.span()); Ok(Atom::Num(num_text.parse().unwrap())) } | 'STRING' { let v = $1.map_err(|_| ())?; let string = &$lexer.span_str(v.span())[1..]; Ok(Atom::String(parse_string(string))) } ; Ident -> Result: 'IDENT' { let v = $1.map_err(|_| ())?; let ident = $lexer.span_str(v.span()).to_string(); Ok(ident) } ; %% use crate::syn::ast::*; type Result = std::result::Result; fn flatten(head: Result>, tail: Result) -> Result> { let mut head = head?; let tail = tail?; head.push(tail); Ok(head) } fn parse_string(input: &str) -> String { let mut s = String::new(); let input = &input[1..input.bytes().len() - 1]; let mut chars = input.chars(); while let Some(c) = chars.next() { if c == '\\' { let next = chars.next().unwrap(); let c = match next { '\\' => '\\', '\'' => '\'', '"' => '"', 'n' => '\n', 't' => '\t', _ => unreachable!(), }; s.push(c); } else { s.push(c); } } s }