use crate::parser::TokenSlice; peg::parser! { grammar script_parser<'a>() for TokenSlice<'a> { use crate::parser::ast::*; use crate::runtime::numbers::Number; use crate::lexer::{ScriptToken, ScriptTokenType}; pub rule program() -> Program = ex:expression_list() eof() { Program(ex) } rule expression_list() -> ExpressionList = e:(expression() ++ ";") term:";"? { ExpressionList { expressions: e, is_void: term.is_some() } } // rule statement() -> Expression // Include conditional here separately from expression to allow "if" without semi // = e:conditional() { Expression::Value(ValueExpression::ConditionalBlock(e)) } // / e:condition_loop() { Expression::Void(VoidExpression::ConditionLoop(e)) } // / e:expression() ";" { e } pub rule expression() -> Expression = ex:value_expression() { Expression::Value(ex) } / ex:void_expression() { Expression::Void(ex) } rule void_expression() -> VoidExpression = ex:print() { VoidExpression::Print(ex) } / "import" "{" items:identifier_list() "}" "from" source:raw_string() { VoidExpression::Import(Import { source, items }) } / "export" "{" items:identifier_list() "}" { VoidExpression::Export(Export { items }) } / e:condition_loop() { VoidExpression::ConditionLoop(e) } #[cache_left_rec] rule value_expression() -> ValueExpression = co:conditional() { ValueExpression::ConditionalBlock(co) } / decl:declare_variable() { ValueExpression::DeclareIdentifier(decl) } / decl:declare_function() { ValueExpression::DeclareFunction(decl) } / name:bare_identifier() "(" params:param_list()? ")" { ValueExpression::FunctionCall(FunctionCall { name, params: params.unwrap_or_default() }) } / left:value_expression() op:binary_operator() right:value_expression() { ValueExpression::Binary { lhs: Box::new(left), rhs: Box::new(right), operator: op } } / op:unary_operator() operand:value_expression() { ValueExpression::Unary { operator: op, operand: Box::new(operand) } } / grouped() / ident:bare_identifier() { ValueExpression::Identifier(ident) } / li:literal() { ValueExpression::Literal(li) } rule grouped() -> ValueExpression = "(" ex:value_expression() ")" { ValueExpression::Grouped(GroupedExpression { inner: Box::new(ex) }) } rule print() -> Print = "print" ex:value_expression() { ex.into() } rule declare_variable() -> DeclareIdent = "let" ex:declare_identifier() { ex } rule declare_identifier() -> DeclareIdent = assign:assignment() { DeclareIdent::WithValue(assign) } / ident:bare_identifier() { DeclareIdent::WithoutValue(ident) } rule declare_function() -> DeclareFunction = "fn" ident:bare_identifier() "(" params:(declare_identifier() ** ",") ")" "{" block:expression_list()? "}" { DeclareFunction { ident, params, body: block.unwrap_or_default() } } rule condition_loop() -> ConditionalLoop = "while" guard:value_expression() "{" block:expression_list()? "}" { ConditionalLoop { block: GuardedBlock { guard: Box::new(guard), block: block.unwrap_or_default() }, fallback: None } } / "while" guard:value_expression() "{" block:expression_list()? "}" "else" "{" fallback:expression_list()? "}" { ConditionalLoop { block: GuardedBlock { guard: Box::new(guard), block: block.unwrap_or_default() }, fallback } } rule conditional() -> Conditional // = bl:guarded_block() { Conditional { fallback: None, blocks: vec![bl] } } = blocks:(guarded_block() ++ "else") "else" "{" fallback:expression_list() "}" { Conditional { blocks, fallback: Some(fallback) } } / blocks:(guarded_block() ++ "else") { Conditional { fallback: None, blocks, } } rule guarded_block() -> GuardedBlock = "if" guard:value_expression() "{" block:expression_list()? "}" { GuardedBlock { block: block.unwrap_or_default(), guard: Box::new(guard) } } rule assignment() -> Assignment = ident:bare_identifier() "=" expr:value_expression() { Assignment { ident, value: Box::new(expr) } } rule binary_operator() -> BinaryOp = "+" { BinaryOp::Add } / "-" { BinaryOp::Subtract } / "*" { BinaryOp::Multiply } / "/" { BinaryOp::Divide } / "%" { BinaryOp::Modulo } rule unary_operator() -> UnaryOp = "!" { UnaryOp::Not } / "-" { UnaryOp::Negate } rule identifier_list() -> IdentifierList = identifier() ++ "," rule param_list() -> ParameterList = ids:value_expression() ++ "," rule identifier() -> IdentifierNode = id:alias_identifier() { IdentifierNode::Alias(id) } / id:bare_identifier() { IdentifierNode::Direct(id) } rule alias_identifier() -> IdentifierAlias = base:bare_identifier() "as" alias:bare_identifier() { IdentifierAlias(base.0, alias.0) } rule bare_identifier() -> Identifier = [ScriptToken { token_type: ScriptTokenType::Identifier(vl), .. }] { Identifier(String::from(*vl)) } rule literal() -> LiteralNode = "true" { LiteralNode::Boolean(true) } / "false" { LiteralNode::Boolean(false) } / "null" { LiteralNode::Null } / [ScriptToken { token_type: ScriptTokenType::String(vl), .. }] { LiteralNode::String(String::from(*vl)) } / [ScriptToken { token_type: ScriptTokenType::OwnedString(vl), .. }] { LiteralNode::String(vl.clone()) } / [ScriptToken { token_type: ScriptTokenType::Integer(vl), .. }] { LiteralNode::Number(Number::Integer(*vl)) } / [ScriptToken { token_type: ScriptTokenType::Float(vl), .. }] { LiteralNode::Number(Number::Float(*vl)) } rule raw_string() -> String = [ScriptToken { token_type: ScriptTokenType::String(vl), .. }] { String::from(*vl) } / [ScriptToken { token_type: ScriptTokenType::OwnedString(vl), .. }] { vl.clone() } rule eof() = ![_] } } pub use script_parser::{expression, program};