From fbb650cb5afdbfec1026463f02d16dbb98acc1e9 Mon Sep 17 00:00:00 2001
From: Louis Capitanchik <contact@louiscap.co>
Date: Tue, 16 May 2023 03:13:50 +0100
Subject: [PATCH] Support function calls

---
 forge-script-lang/src/parser/ast.rs            | 11 ++++++++++-
 forge-script-lang/src/parser/grammar.rs        |  5 +++--
 forge-script-lang/src/parser/test_suite.rs     |  9 +++++++++
 .../src/runtime/executor/printer.rs            | 18 ++++++++++++++++++
 4 files changed, 40 insertions(+), 3 deletions(-)

diff --git a/forge-script-lang/src/parser/ast.rs b/forge-script-lang/src/parser/ast.rs
index 739cacb..460fc41 100644
--- a/forge-script-lang/src/parser/ast.rs
+++ b/forge-script-lang/src/parser/ast.rs
@@ -167,6 +167,7 @@ pub enum ValueExpression {
 	Assignment(Assignment),
 	ConditionalBlock(Conditional),
 	Identifier(Identifier),
+	FunctionCall(FunctionCall),
 }
 
 #[derive(Clone)]
@@ -238,7 +239,7 @@ impl From<ValueExpression> for Print {
 }
 
 pub type IdentifierList = Vec<IdentifierNode>;
-pub type ParameterList = Vec<IdentifierNode>;
+pub type ParameterList = Vec<ValueExpression>;
 
 #[derive(Clone)]
 #[cfg_attr(feature = "debug-ast", derive(Debug))]
@@ -342,3 +343,11 @@ pub enum DeclareIdent {
 	WithValue(Assignment),
 	WithoutValue(Identifier),
 }
+
+#[derive(Clone)]
+#[cfg_attr(feature = "debug-ast", derive(Debug))]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+pub struct FunctionCall {
+	pub name: Identifier,
+	pub params: ParameterList,
+}
diff --git a/forge-script-lang/src/parser/grammar.rs b/forge-script-lang/src/parser/grammar.rs
index 65f9677..3d8117f 100644
--- a/forge-script-lang/src/parser/grammar.rs
+++ b/forge-script-lang/src/parser/grammar.rs
@@ -33,6 +33,8 @@ peg::parser! {
 			= "(" ex:value_expression() ")" { ValueExpression::Grouped(GroupedExpression { inner: Box::new(ex) }) }
 			/ co:conditional() { ValueExpression::ConditionalBlock(co) }
 			/ decl:declare_variable() { ValueExpression::DeclareIdentifier(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()
@@ -85,8 +87,7 @@ peg::parser! {
 			= identifier() ++ ","
 
 		rule param_list() -> ParameterList
-			= ids:bare_identifier() ++ ","
-				{ ids.iter().cloned().map(IdentifierNode::Direct).collect() }
+			= ids:value_expression() ++ ","
 
 		rule identifier() -> IdentifierNode
 			= id:alias_identifier() { IdentifierNode::Alias(id) }
diff --git a/forge-script-lang/src/parser/test_suite.rs b/forge-script-lang/src/parser/test_suite.rs
index 52a0627..eaec0bc 100644
--- a/forge-script-lang/src/parser/test_suite.rs
+++ b/forge-script-lang/src/parser/test_suite.rs
@@ -49,3 +49,12 @@ fn expression_list() {
 	parse_program("if flop { 123 + 2323 } else { let boo = false }; let glob = \"swarmp\"")
 		.expect("Failed simple expression list");
 }
+
+#[test]
+fn function_calls() {
+	parse_program("some_func()").expect("Failed empty function params");
+	parse_program("some_func(123)").expect("Failed single function param");
+	parse_program("some_func(false, 2929)").expect("Failed multiple function params");
+	parse_program("some_func(250, if true { \"some val\" } else { \"123\" })")
+		.expect("Failed complex function params");
+}
diff --git a/forge-script-lang/src/runtime/executor/printer.rs b/forge-script-lang/src/runtime/executor/printer.rs
index 0c05b5d..078f8a2 100644
--- a/forge-script-lang/src/runtime/executor/printer.rs
+++ b/forge-script-lang/src/runtime/executor/printer.rs
@@ -143,6 +143,24 @@ impl Visitor for TreePrinter {
 			ValueExpression::Identifier(ident) => {
 				self.write(ident);
 			}
+			ValueExpression::FunctionCall(call) => {
+				self.write(&call.name);
+				self.write("(");
+				let list = call
+					.params
+					.iter()
+					.map(|param| {
+						let mut inner = TreePrinter {
+							indent: self.indent,
+							buffer: String::new(),
+						};
+						inner.evaluate_value_expression(param);
+						inner.take_value()
+					})
+					.collect::<Vec<String>>();
+				self.write(list.as_slice().join(", "));
+				self.write(")");
+			}
 		}
 	}
 
-- 
GitLab