Skip to content
Snippets Groups Projects
grammar.rs 6.93 KiB
Newer Older
Louis's avatar
Louis committed
use crate::parser::TokenSlice;

peg::parser! {
	grammar forge_parser<'a>() for TokenSlice<'a> {
Louis's avatar
Louis committed
		use crate::parser::ast::*;
		use crate::runtime::numbers::Number;
		use crate::lexer::{ScriptToken, ScriptTokenType};

		pub rule program() -> Program
			= ex:expression_list() eof() { Program(ex) }

		pub rule expression() -> Expression
Louis's avatar
Louis committed
			= ex:value_expression() { Expression::Value(ex) }
Louis's avatar
Louis committed
			/ ex:void_expression() { Expression::Void(ex) }

		rule void_expression() -> VoidExpression
			= ex:print() { VoidExpression::Print(ex) }
			/ "import" "{" items:identifier_list() "}" "from" source:string_value() { VoidExpression::Import(Import { source, items }) }
			/ "export" "{" items:identifier_list() "}" { VoidExpression::Export(Export { items }) }
			/ e:condition_loop() { VoidExpression::ConditionLoop(e) }
Louis's avatar
Louis committed

Louis's avatar
Louis committed
		rule value_expression() -> ValueExpression
			=
			co:conditional_statement() { ValueExpression::ConditionalBlock(co) }
			/ t:type_of() { ValueExpression::Typeof(t) }
Louis's avatar
Louis committed
			// / binary_expression()
			/ decl:declare_variable() { ValueExpression::DeclareIdentifier(decl) }
Louis's avatar
Louis committed
			/ decl:declare_function() { ValueExpression::DeclareFunction(decl) }
			/ name:simple_identifier() "(" params:param_list()? ")"
Louis's avatar
Louis committed
				{ ValueExpression::FunctionCall(FunctionCall { name, params: params.unwrap_or_default() }) }
Louis's avatar
Louis committed
			/ op:unary_operator() operand:value_expression()
				{ ValueExpression::Unary { operator: op, operand: Box::new(operand) } }
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 } }
			/ ident:simple_identifier() !"(" { ValueExpression::Identifier(ident) }
Louis's avatar
Louis committed
			/ li:literal() { ValueExpression::Literal(li) }

		rule grouped() -> ValueExpression
			= "(" ex:value_expression() ")"
				{ ValueExpression::Grouped(GroupedExpression { inner: Box::new(ex) }) }

Louis's avatar
Louis committed
		rule print() -> Print
			= "print" ex:value_expression() { ex.into() }

		rule type_of() -> TypeofValue
			= "typeof" ex:value_expression() { TypeofValue(Box::new(ex)) }
		rule declare_function() -> DeclareFunction
			= "fn" ident:simple_identifier() "(" params:(function_param() ** ",") ")" body:block()
				{ DeclareFunction { ident, params, body } }

		rule function_param() -> DeclareIdent
Louis's avatar
Louis committed
			= assign:assignment() { DeclareIdent::WithValue(assign) }
			/ ident:simple_identifier() { DeclareIdent::WithoutValue(ident) }
		rule assignment() -> Assignment
			= ident:simple_identifier() "=" ex:value_expression() { Assignment { ident, value: Box::new(ex) } }

		rule declare_variable() -> DeclareIdent
			= "let" assign:assignment() { DeclareIdent::WithValue(assign) }
			/ "let" ident:simple_identifier() { DeclareIdent::WithoutValue(ident) }
Louis's avatar
Louis committed
		rule condition_loop() -> ConditionalLoop
			= "while" guard:value_expression() block:block()
				{ ConditionalLoop { block: GuardedBlock { guard: Box::new(guard), block }, fallback: None } }
Louis's avatar
Louis committed
			/ "while" guard:value_expression() block:block() "finally" fallback:block()
				{ ConditionalLoop { block: GuardedBlock { guard: Box::new(guard), block }, fallback: Some(fallback) } }

		rule conditional_statement() -> Conditional
			= blocks:(conditional_block() ++ "else") "else" fallback:block()
				{ Conditional { blocks, fallback: Some(fallback) } }
			/ blocks:(conditional_block() ++ "else")
				{ Conditional { blocks, fallback: None } }

		rule conditional_block() -> GuardedBlock
			= "if" guard:value_expression() "{" block:expression_list()? "}"
				{ GuardedBlock { block: block.unwrap_or_default(), guard: Box::new(guard) } }

		rule block() -> ExpressionList
			= "{" ex:expression_list() "}" { ex }

		rule expression_list() -> ExpressionList
			= ex:(expression() ** ";") term:";"? { ExpressionList { expressions: ex, is_void: term.is_some() } }
Louis's avatar
Louis committed

Louis's avatar
Louis committed
		// #[cache_left_rec]
		// rule precedence_expression() -> ValueExpression
		// 	= precedence! {
		// 		val:value_expression() { val }
		// 		"-" z:(@) { ValueExpression::Unary { operator: UnaryOp::Negate, operand: Box::new(z) } }
		// 		--
		// 		x:(@) "+" y:@ { ValueExpression::Binary { operator: BinaryOp::Add, lhs: Box::new(x), rhs: Box::new(y) } }
		// 		x:(@) "-" y:@ { ValueExpression::Binary { operator: BinaryOp::Subtract, lhs: Box::new(x), rhs: Box::new(y) } }
		// 		--
		// 		x:(@) "*" y:@ { ValueExpression::Binary { operator: BinaryOp::Multiply, lhs: Box::new(x), rhs: Box::new(y) } }
		// 		x:(@) "/" y:@ { ValueExpression::Binary { operator: BinaryOp::Divide, lhs: Box::new(x), rhs: Box::new(y) } }
		// 		x:(@) "%" y:@ { ValueExpression::Binary { operator: BinaryOp::Modulo, lhs: Box::new(x), rhs: Box::new(y) } }
		// 		--
		// 		x:(@) "==" y:@ { ValueExpression::Binary { operator: BinaryOp::Equals, lhs: Box::new(x), rhs: Box::new(y) } }
		// 		x:(@) "&&" y:@ { ValueExpression::Binary { operator: BinaryOp::BoolAnd, lhs: Box::new(x), rhs: Box::new(y) } }
		// 		x:(@) "||" y:@ { ValueExpression::Binary { operator: BinaryOp::BoolOr, lhs: Box::new(x), rhs: Box::new(y) } }
		// 		--
		// 		"!" z:(@) { ValueExpression::Unary { operator: UnaryOp::Not, operand: Box::new(z) } }
		// 		--
		// 		"(" ex:precedence_expression() ")" { ValueExpression::Grouped(GroupedExpression { inner: Box::new(ex) }) }
		// 	}

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

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

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

		rule param_list() -> ParameterList
			= value_expression() ++ ","
Louis's avatar
Louis committed

		rule identifier() -> IdentifierNode
			= id:alias_identifier() { IdentifierNode::Alias(id) }
			/ id:simple_identifier() { IdentifierNode::Direct(id) }
Louis's avatar
Louis committed

		rule alias_identifier() -> IdentifierAlias
			= base:simple_identifier() "as" alias:simple_identifier() { IdentifierAlias(base.0, alias.0) }
Louis's avatar
Louis committed

		rule simple_identifier() -> Identifier
Louis's avatar
Louis committed
			= [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 string_value() -> 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 forge_parser::{expression, program};