diff --git a/forge-script-lang/src/parser/ast.rs b/forge-script-lang/src/parser/ast.rs index 460fc416539abae3deb2b68d7812f2247fd3bec6..644913e996c8313a252775e7ea5a9a1d62cf6383 100644 --- a/forge-script-lang/src/parser/ast.rs +++ b/forge-script-lang/src/parser/ast.rs @@ -168,6 +168,7 @@ pub enum ValueExpression { ConditionalBlock(Conditional), Identifier(Identifier), FunctionCall(FunctionCall), + DeclareFunction(DeclareFunction), } #[derive(Clone)] @@ -344,6 +345,15 @@ pub enum DeclareIdent { WithoutValue(Identifier), } +#[derive(Clone)] +#[cfg_attr(feature = "debug-ast", derive(Debug))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct DeclareFunction { + pub ident: Identifier, + pub params: Vec<DeclareIdent>, + pub body: ExpressionList, +} + #[derive(Clone)] #[cfg_attr(feature = "debug-ast", derive(Debug))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] diff --git a/forge-script-lang/src/parser/grammar.rs b/forge-script-lang/src/parser/grammar.rs index 3d8117f7f1b5a673246efc6b14d6ee4999886f34..59fba41f3ac105314207c34d4c45a098d1e7baaf 100644 --- a/forge-script-lang/src/parser/grammar.rs +++ b/forge-script-lang/src/parser/grammar.rs @@ -33,6 +33,7 @@ peg::parser! { = "(" ex:value_expression() ")" { ValueExpression::Grouped(GroupedExpression { inner: Box::new(ex) }) } / 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() @@ -46,8 +47,15 @@ peg::parser! { = "print" ex:value_expression() { ex.into() } rule declare_variable() -> DeclareIdent - = "let" assign:assignment() { DeclareIdent::WithValue(assign) } - / "let" ident:bare_identifier() { DeclareIdent::WithoutValue(ident) } + = "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()? "}" diff --git a/forge-script-lang/src/parser/test_suite.rs b/forge-script-lang/src/parser/test_suite.rs index eaec0bc7daa1fbed20bc338e7f87dc6102d2272c..0b00afaa84561bcb6b63bf829e0c3d2cb9d3f546 100644 --- a/forge-script-lang/src/parser/test_suite.rs +++ b/forge-script-lang/src/parser/test_suite.rs @@ -58,3 +58,14 @@ fn function_calls() { parse_program("some_func(250, if true { \"some val\" } else { \"123\" })") .expect("Failed complex function params"); } + +#[test] +fn declare_function() { + parse_program("fn some_func() {}").expect("Failed basic function"); + parse_program("fn some_func(my_param) {}").expect("Failed basic function"); + parse_program("fn some_func(my_param, second) {}").expect("Failed basic function"); + parse_program("fn some_func(my_param, second = 1234) {}") + .expect("Failed basic function with defaults"); + parse_program("fn add(first, second) { first + second }") + .expect("Failed basic function with body"); +} diff --git a/forge-script-lang/src/runtime/executor/printer.rs b/forge-script-lang/src/runtime/executor/printer.rs index 078f8a29a6ed91405361cbf66d20ed3fc20bd8e3..4f95e61c95b24e80170afa2928bc54f50ccc7102 100644 --- a/forge-script-lang/src/runtime/executor/printer.rs +++ b/forge-script-lang/src/runtime/executor/printer.rs @@ -161,6 +161,39 @@ impl Visitor for TreePrinter { self.write(list.as_slice().join(", ")); self.write(")"); } + ValueExpression::DeclareFunction(declare) => { + self.write("fn "); + self.write(&declare.ident); + self.write("("); + let list = declare + .params + .iter() + .map(|param| { + let mut inner = TreePrinter { + indent: self.indent, + buffer: String::new(), + }; + + match param { + DeclareIdent::WithValue(assign) => { + inner.write(&assign.ident); + inner.write(" = "); + inner.evaluate_value_expression(assign.value.as_ref()); + } + DeclareIdent::WithoutValue(identifier) => inner.write(identifier), + } + + inner.take_value() + }) + .collect::<Vec<String>>(); + + self.write(list.as_slice().join(", ")); + self.writeln(") {"); + self.increment(); + self.writeln(self.format_expression_list(&declare.body)); + self.decrement(); + self.writeln("}"); + } } }