Skip to content
Snippets Groups Projects
grammar.rs 5.56 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:(expression() ++ ";") term:";"? { ExpressionList { expressions: e, is_void: term.is_some() } }
Louis's avatar
Louis committed

		// 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 }
Louis's avatar
Louis committed

		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) }
Louis's avatar
Louis committed

		 #[cache_left_rec]
		rule value_expression() -> ValueExpression
			= "(" ex:value_expression() ")" { ValueExpression::Grouped(GroupedExpression { inner: Box::new(ex) }) }
			/ co:conditional() { ValueExpression::ConditionalBlock(co) }
			/ decl:declare_variable() { ValueExpression::DeclareIdentifier(decl) }
Louis's avatar
Louis committed
			/ decl:declare_function() { ValueExpression::DeclareFunction(decl) }
Louis's avatar
Louis committed
			/ name:bare_identifier() "(" params:param_list()? ")"
				{ ValueExpression::FunctionCall(FunctionCall { name, params: params.unwrap_or_default() }) }
Louis's avatar
Louis committed
			/ 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) } }
			/ ident:bare_identifier() { ValueExpression::Identifier(ident) }
Louis's avatar
Louis committed
			/ li:literal() { ValueExpression::Literal(li) }

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

		rule declare_variable() -> DeclareIdent
Louis's avatar
Louis committed
			= "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() } }
Louis's avatar
Louis committed
		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 } }
Louis's avatar
Louis committed

		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) } }
Louis's avatar
Louis committed

		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
Louis's avatar
Louis committed
			= ids:value_expression() ++ ","
Louis's avatar
Louis committed

		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() }

Louis's avatar
Louis committed
		rule eof() = ![_]
	}
}

pub use script_parser::{expression, program};