Skip to content
Snippets Groups Projects
grammar.rs 4.09 KiB
Newer Older
Louis's avatar
Louis committed
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:(statement() / 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) }

		 #[cache_left_rec]
		rule value_expression() -> ValueExpression
			= "(" ex:value_expression() ")" { ValueExpression::Grouped(GroupedExpression { inner: Box::new(ex) }) }
			/ co:conditional() { ValueExpression::ConditionalBlock(co) }
			/ 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) } }
			/ li:literal() { ValueExpression::Literal(li) }

		rule print() -> Print
			= "print" ex:value_expression() { ex.into() }

		rule condition_loop() -> ConditionalLoop
			= "while" guard:value_expression() "{" block:expression_list() "}" { ConditionalLoop { block: GuardedBlock { guard: Box::new(guard), block}, fallback: None } }
			/ "while" guard:value_expression() "{" block:expression_list() "}" "else" "{" fallback:expression_list() "}"
				{ ConditionalLoop { block: GuardedBlock { guard: Box::new(guard), block}, fallback: Some(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, guard: Box::new(guard) } }

		rule binary_operator() -> BinaryOp
			= "+" { BinaryOp::Add }
			/ "-" { BinaryOp::Subtract }
			/ "*" { BinaryOp::Multiply }
			/ "/" { BinaryOp::Divide }
			/ "%" { BinaryOp::Modulo }
Louis's avatar
Louis committed

		rule unary_operator() -> UnaryOp
			= "!" { UnaryOp::Not }
			/ "-" { UnaryOp::Negate }

		rule identifier_list() -> IdentifierList
			= identifier() ++ ","

		rule param_list() -> ParameterList
			= ids:bare_identifier() ++ ","
				{ ids.iter().cloned().map(IdentifierNode::Direct).collect() }

		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 eof() = ![_]
	}
}

pub use script_parser::{expression, program};