Skip to content
Snippets Groups Projects
Verified Commit ab558877 authored by Louis's avatar Louis :fire:
Browse files

Support return statements in AST, function declerations in grammar, add full file parsing tests

parent f2492b6f
No related branches found
No related tags found
No related merge requests found
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
<sourceFolder url="file://$MODULE_DIR$/forge-script-web/src" isTestSource="false" /> <sourceFolder url="file://$MODULE_DIR$/forge-script-web/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/forge-script/src" isTestSource="false" /> <sourceFolder url="file://$MODULE_DIR$/forge-script/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" /> <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/forge-script-lang/tests" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/target" /> <excludeFolder url="file://$MODULE_DIR$/target" />
</content> </content>
<orderEntry type="inheritedJdk" /> <orderEntry type="inheritedJdk" />
......
...@@ -7,13 +7,21 @@ pub trait AstNode {} ...@@ -7,13 +7,21 @@ pub trait AstNode {}
#[derive(Clone)] #[derive(Clone)]
#[cfg_attr(any(feature = "debug-ast", test), derive(Debug))] #[cfg_attr(any(feature = "debug-ast", test), derive(Debug))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(
feature = "serde",
derive(serde::Serialize, serde::Deserialize),
serde(rename_all = "snake_case")
)]
pub struct Program(pub ExpressionList); pub struct Program(pub ExpressionList);
impl AstNode for Program {} impl AstNode for Program {}
#[derive(Clone)] #[derive(Clone)]
#[cfg_attr(any(feature = "debug-ast", test), derive(Debug))] #[cfg_attr(any(feature = "debug-ast", test), derive(Debug))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(
feature = "serde",
derive(serde::Serialize, serde::Deserialize),
serde(rename_all = "snake_case")
)]
pub struct ExpressionList { pub struct ExpressionList {
pub expressions: Vec<Expression>, pub expressions: Vec<Expression>,
pub is_void: bool, pub is_void: bool,
...@@ -155,11 +163,16 @@ pub enum VoidExpression { ...@@ -155,11 +163,16 @@ pub enum VoidExpression {
Import(Import), Import(Import),
Export(Export), Export(Export),
Print(Print), Print(Print),
Return(Return),
} }
#[derive(Clone)] #[derive(Clone)]
#[cfg_attr(any(feature = "debug-ast", test), derive(Debug))] #[cfg_attr(any(feature = "debug-ast", test), derive(Debug))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(
feature = "serde",
derive(serde::Serialize, serde::Deserialize),
serde(rename_all = "snake_case")
)]
pub struct GroupedExpression { pub struct GroupedExpression {
pub inner: Box<ValueExpression>, pub inner: Box<ValueExpression>,
} }
...@@ -297,12 +310,20 @@ impl ValueExpression { ...@@ -297,12 +310,20 @@ impl ValueExpression {
#[derive(Clone)] #[derive(Clone)]
#[cfg_attr(any(feature = "debug-ast", test), derive(Debug))] #[cfg_attr(any(feature = "debug-ast", test), derive(Debug))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(
feature = "serde",
derive(serde::Serialize, serde::Deserialize),
serde(rename_all = "snake_case")
)]
pub struct TypeofValue(pub Box<ValueExpression>); pub struct TypeofValue(pub Box<ValueExpression>);
#[derive(Clone)] #[derive(Clone)]
#[cfg_attr(any(feature = "debug-ast", test), derive(Debug))] #[cfg_attr(any(feature = "debug-ast", test), derive(Debug))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(
feature = "serde",
derive(serde::Serialize, serde::Deserialize),
serde(rename_all = "snake_case")
)]
pub struct ConditionalLoop { pub struct ConditionalLoop {
pub block: GuardedBlock, pub block: GuardedBlock,
pub fallback: Option<ExpressionList>, pub fallback: Option<ExpressionList>,
...@@ -325,7 +346,11 @@ impl ConditionalLoop { ...@@ -325,7 +346,11 @@ impl ConditionalLoop {
#[derive(Clone)] #[derive(Clone)]
#[cfg_attr(any(feature = "debug-ast", test), derive(Debug))] #[cfg_attr(any(feature = "debug-ast", test), derive(Debug))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(
feature = "serde",
derive(serde::Serialize, serde::Deserialize),
serde(rename_all = "snake_case")
)]
pub struct Conditional { pub struct Conditional {
pub blocks: Vec<GuardedBlock>, pub blocks: Vec<GuardedBlock>,
pub fallback: Option<ExpressionList>, pub fallback: Option<ExpressionList>,
...@@ -333,7 +358,11 @@ pub struct Conditional { ...@@ -333,7 +358,11 @@ pub struct Conditional {
#[derive(Clone)] #[derive(Clone)]
#[cfg_attr(any(feature = "debug-ast", test), derive(Debug))] #[cfg_attr(any(feature = "debug-ast", test), derive(Debug))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(
feature = "serde",
derive(serde::Serialize, serde::Deserialize),
serde(rename_all = "snake_case")
)]
pub struct GuardedBlock { pub struct GuardedBlock {
pub guard: Box<ValueExpression>, pub guard: Box<ValueExpression>,
pub block: ExpressionList, pub block: ExpressionList,
...@@ -341,7 +370,11 @@ pub struct GuardedBlock { ...@@ -341,7 +370,11 @@ pub struct GuardedBlock {
#[derive(Clone)] #[derive(Clone)]
#[cfg_attr(any(feature = "debug-ast", test), derive(Debug))] #[cfg_attr(any(feature = "debug-ast", test), derive(Debug))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(
feature = "serde",
derive(serde::Serialize, serde::Deserialize),
serde(rename_all = "snake_case")
)]
pub struct Import { pub struct Import {
pub source: String, pub source: String,
pub items: IdentifierList, pub items: IdentifierList,
...@@ -349,14 +382,22 @@ pub struct Import { ...@@ -349,14 +382,22 @@ pub struct Import {
#[derive(Clone)] #[derive(Clone)]
#[cfg_attr(any(feature = "debug-ast", test), derive(Debug))] #[cfg_attr(any(feature = "debug-ast", test), derive(Debug))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(
feature = "serde",
derive(serde::Serialize, serde::Deserialize),
serde(rename_all = "snake_case")
)]
pub struct Export { pub struct Export {
pub items: IdentifierList, pub items: IdentifierList,
} }
#[derive(Clone)] #[derive(Clone)]
#[cfg_attr(any(feature = "debug-ast", test), derive(Debug))] #[cfg_attr(any(feature = "debug-ast", test), derive(Debug))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(
feature = "serde",
derive(serde::Serialize, serde::Deserialize),
serde(rename_all = "snake_case")
)]
pub struct Print { pub struct Print {
pub expr: Box<ValueExpression>, pub expr: Box<ValueExpression>,
} }
...@@ -368,12 +409,34 @@ impl From<ValueExpression> for Print { ...@@ -368,12 +409,34 @@ impl From<ValueExpression> for Print {
} }
} }
#[derive(Clone)]
#[cfg_attr(any(feature = "debug-ast", test), derive(Debug))]
#[cfg_attr(
feature = "serde",
derive(serde::Serialize, serde::Deserialize),
serde(rename_all = "snake_case")
)]
pub struct Return {
pub expr: Box<ValueExpression>,
}
impl From<ValueExpression> for Return {
fn from(value: ValueExpression) -> Self {
Return {
expr: Box::new(value),
}
}
}
pub type IdentifierList = Vec<IdentifierNode>; pub type IdentifierList = Vec<IdentifierNode>;
pub type ParameterList = Vec<ValueExpression>; pub type ParameterList = Vec<ValueExpression>;
#[derive(Clone)] #[derive(Clone)]
#[cfg_attr(any(feature = "debug-ast", test), derive(Debug))] #[cfg_attr(any(feature = "debug-ast", test), derive(Debug))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(
feature = "serde",
derive(serde::Serialize, serde::Deserialize),
serde(rename_all = "snake_case")
)]
pub struct Identifier(pub String); pub struct Identifier(pub String);
impl Display for Identifier { impl Display for Identifier {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
...@@ -385,7 +448,11 @@ impl Display for Identifier { ...@@ -385,7 +448,11 @@ impl Display for Identifier {
/// IdentifierAlias(original, alias) => identifier "as" alias /// IdentifierAlias(original, alias) => identifier "as" alias
#[derive(Clone)] #[derive(Clone)]
#[cfg_attr(any(feature = "debug-ast", test), derive(Debug))] #[cfg_attr(any(feature = "debug-ast", test), derive(Debug))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(
feature = "serde",
derive(serde::Serialize, serde::Deserialize),
serde(rename_all = "snake_case")
)]
pub struct IdentifierAlias(pub String, pub String); pub struct IdentifierAlias(pub String, pub String);
impl Display for IdentifierAlias { impl Display for IdentifierAlias {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
...@@ -568,5 +635,6 @@ impl_into_void_expr!( ...@@ -568,5 +635,6 @@ impl_into_void_expr!(
Print => VoidExpression::Print, Print => VoidExpression::Print,
ConditionalLoop => VoidExpression::ConditionLoop, ConditionalLoop => VoidExpression::ConditionLoop,
Import => VoidExpression::Import, Import => VoidExpression::Import,
Export => VoidExpression::Export Export => VoidExpression::Export,
Return => VoidExpression::Return
); );
...@@ -25,18 +25,22 @@ ExpressionList: ExpressionList = { ...@@ -25,18 +25,22 @@ ExpressionList: ExpressionList = {
}, },
} }
FunctionDeclParamList = CommaSeperatedList<FunctionDeclParam>;
FunctionDeclParam: DeclareIdent = {
Identifier => DeclareIdent::WithoutValue(<>),
<id:Identifier> "=" <def:ValueExpression> => DeclareIdent::WithValue(Assignment {
ident: id,
value: Box::new(def),
}),
}
ParameterList: Vec<ValueExpression> = { ParameterList: Vec<ValueExpression> = {
"(" <CommaSeperatedValueExpressionList> ")" => <>, "(" <CommaSeperatedValueExpressionList> ")" => <>,
"(" ")" => Vec::new(), "(" ")" => Vec::new(),
} }
CommaSeperatedValueExpressionList: Vec<ValueExpression> = { CommaSeperatedValueExpressionList = CommaSeperatedList<ValueExpression>;
ValueExpression => vec![<>],
<mut ls:CommaSeperatedValueExpressionList> "," <ve:ValueExpression> => {
ls.push(ve);
ls
},
}
pub Expression: Expression = { pub Expression: Expression = {
ValueExpression => Expression::Value(<>), ValueExpression => Expression::Value(<>),
...@@ -44,9 +48,9 @@ pub Expression: Expression = { ...@@ -44,9 +48,9 @@ pub Expression: Expression = {
} }
ValueExpression: ValueExpression = { ValueExpression: ValueExpression = {
BoolOrExpression => <>, FirstClassDeclareExpression => <>,
"let" <Identifier> => DeclareIdent::WithoutValue(<>).into(), "let" <Identifier> => DeclareIdent::WithoutValue(<>).into(),
"let" <id:Identifier> "=" <expr:BoolOrExpression> => DeclareIdent::WithValue( "let" <id:Identifier> "=" <expr:FirstClassDeclareExpression> => DeclareIdent::WithValue(
Assignment { ident: id, value :Box::new(expr) } Assignment { ident: id, value :Box::new(expr) }
).into(), ).into(),
} }
...@@ -55,6 +59,11 @@ VoidExpression: VoidExpression = { ...@@ -55,6 +59,11 @@ VoidExpression: VoidExpression = {
UnaryStatement => <> UnaryStatement => <>
} }
FirstClassDeclareExpression: ValueExpression = {
BoolOrExpression => <>,
DeclareFunction => <>.into(),
}
BoolOrExpression: ValueExpression = { BoolOrExpression: ValueExpression = {
BoolAndExpression => <>, BoolAndExpression => <>,
<lhs:BoolOrExpression> "||" <rhs:BoolAndExpression> => ValueExpression::bool_or(lhs, rhs), <lhs:BoolOrExpression> "||" <rhs:BoolAndExpression> => ValueExpression::bool_or(lhs, rhs),
...@@ -93,7 +102,8 @@ MultiplicativeExpression: ValueExpression = { ...@@ -93,7 +102,8 @@ MultiplicativeExpression: ValueExpression = {
} }
UnaryStatement: VoidExpression = { UnaryStatement: VoidExpression = {
"print" <UnaryExpression> => Print { expr: Box::new(<>) }.into(), "print" <FirstClassDeclareExpression> => Print { expr: Box::new(<>) }.into(),
"return" <FirstClassDeclareExpression> => Return { expr: Box::new(<>) }.into(),
} }
UnaryExpression: ValueExpression = { UnaryExpression: ValueExpression = {
...@@ -123,6 +133,14 @@ BaseExpression: ValueExpression = { ...@@ -123,6 +133,14 @@ BaseExpression: ValueExpression = {
"(" <ValueExpression> ")" => GroupedExpression { inner: Box::new(<>) }.into(), "(" <ValueExpression> ")" => GroupedExpression { inner: Box::new(<>) }.into(),
} }
DeclareFunction: DeclareFunction = {
"fn" <ident:Identifier> "(" <params:FunctionDeclParamList?> ")" <body:ExpressionBlock> => DeclareFunction {
ident,
params: params.unwrap_or_default(),
body,
}
}
AccessorChainAnyEnding: Vec<AccessorType> = { AccessorChainAnyEnding: Vec<AccessorType> = {
AccessorChainEndingWithProperty => <>, AccessorChainEndingWithProperty => <>,
FunctionCall => vec![AccessorType::FunctionCall(<>)], FunctionCall => vec![AccessorType::FunctionCall(<>)],
...@@ -198,6 +216,14 @@ StringValue: String = { ...@@ -198,6 +216,14 @@ StringValue: String = {
"owned_string" => <>, "owned_string" => <>,
}; };
CommaSeperatedList<Type>: Vec<Type> = {
Type => vec![<>],
<mut ls:CommaSeperatedList<Type>> "," <ty:Type> => {
ls.push(ty);
ls
}
}
extern { extern {
type Location = usize; type Location = usize;
type Error = String; type Error = String;
...@@ -212,6 +238,7 @@ extern { ...@@ -212,6 +238,7 @@ extern {
"-" => ScriptTokenType::Minus, "-" => ScriptTokenType::Minus,
"+" => ScriptTokenType::Plus, "+" => ScriptTokenType::Plus,
";" => ScriptTokenType::Semicolon, ";" => ScriptTokenType::Semicolon,
":" => ScriptTokenType::Colon,
"/" => ScriptTokenType::Slash, "/" => ScriptTokenType::Slash,
"*" => ScriptTokenType::Asterisk, "*" => ScriptTokenType::Asterisk,
"!" => ScriptTokenType::Bang, "!" => ScriptTokenType::Bang,
......
...@@ -56,6 +56,10 @@ mod grammar_test { ...@@ -56,6 +56,10 @@ mod grammar_test {
#[test_case("foo.bar().baz" => matches Ok(_) ; "nested property accessor")] #[test_case("foo.bar().baz" => matches Ok(_) ; "nested property accessor")]
#[test_case("if something_else {}.bar" => matches Ok(_) ; "expression property accessor")] #[test_case("if something_else {}.bar" => matches Ok(_) ; "expression property accessor")]
#[test_case("123 + if if foo { bar } else { baz } { 200 } else { 300 }" => matches Ok(_) ; "Parse inline condition")] #[test_case("123 + if if foo { bar } else { baz } { 200 } else { 300 }" => matches Ok(_) ; "Parse inline condition")]
#[test_case("fn add(a, b) { a + b }" => matches Ok(_) ; "Declare basic fn")]
#[test_case("fn has_no_params() {}" => matches Ok(_) ; "Declare empty params fn")]
#[test_case("fn do_some_stuff(foo = 123, bar, baz = \"My Default String\") {}" => matches Ok(_) ; "Declare complex fn")]
#[test_case("let som_value = fn do_some_stuff(foo = 123, bar, baz = \"My Default String\") {}" => matches Ok(_) ; "First class fn")]
fn expression_parsing(prog: &str) -> Result<Expression, String> { fn expression_parsing(prog: &str) -> Result<Expression, String> {
parse_expression(prog).map(|expr| { parse_expression(prog).map(|expr| {
dbg!(&expr); dbg!(&expr);
......
...@@ -268,6 +268,10 @@ impl Visitor for TreePrinter { ...@@ -268,6 +268,10 @@ impl Visitor for TreePrinter {
self.write("print "); self.write("print ");
self.evaluate_value_expression(print.expr.as_ref()); self.evaluate_value_expression(print.expr.as_ref());
} }
VoidExpression::Return(ret) => {
self.write("return ");
self.evaluate_value_expression(ret.expr.as_ref());
}
} }
Ok(()) Ok(())
......
use crate::parse::ast::{ use crate::parse::ast::{
BinaryOp, DeclareFunction, DeclareIdent, ExpressionList, Print, Program, UnaryOp, BinaryOp, DeclareFunction, DeclareIdent, ExpressionList, Print, Program, Return, UnaryOp,
ValueExpression, VoidExpression, ValueExpression, VoidExpression,
}; };
use crate::parser::ast::Assignment; use crate::parser::ast::Assignment;
...@@ -241,6 +241,10 @@ impl Visitor for SimpleExecutor { ...@@ -241,6 +241,10 @@ impl Visitor for SimpleExecutor {
let value = self.evaluate_value_expression(expr.as_ref())?; let value = self.evaluate_value_expression(expr.as_ref())?;
println!("{}", value); println!("{}", value);
} }
VoidExpression::Return(Return { expr }) => {
let value = self.evaluate_value_expression(expr.as_ref())?;
eprintln!("Currently Unsupported: Return. Value: {}", value);
}
} }
Ok(ForgeValue::Null) Ok(ForgeValue::Null)
......
...@@ -124,6 +124,7 @@ impl Visitor for ChunkBuilder { ...@@ -124,6 +124,7 @@ impl Visitor for ChunkBuilder {
VoidExpression::Import(_) => Err(VmError::chunk_parser("Unsupported expression")), VoidExpression::Import(_) => Err(VmError::chunk_parser("Unsupported expression")),
VoidExpression::Export(_) => Err(VmError::chunk_parser("Unsupported expression")), VoidExpression::Export(_) => Err(VmError::chunk_parser("Unsupported expression")),
VoidExpression::Print(_) => Err(VmError::chunk_parser("Unsupported expression")), VoidExpression::Print(_) => Err(VmError::chunk_parser("Unsupported expression")),
VoidExpression::Return(_) => Err(VmError::chunk_parser("Unsupported expression")),
} }
} }
......
2 + 2;
3 + 3 - 4 * 5;
3 + (3 - 2);
4+4+4 + 4 + 4 + 4;
import { random_float, random_int, random_bool } from "@runtime:random"
import { truncate } from "@runtime:maths"
let var_a = random_int(0, 10) >= 5;
let var_b = random_bool();
let my_var = if if 1 + 2 > 4 { var_a } else { var_b } {
truncate(random_float() * 10)
} else {
random_int(100, 200)
};
\ No newline at end of file
let foo;
let bar = 123 + foo;
fn my_func() {
let foo;
let bar = 123 + foo;
};
let other_name = fn some_internal_name(a, b = 2) {
a * b
};
fn has_early_return(a, b, c) {
if b {
return false;
};
a + c
}
\ No newline at end of file
use forge_script_lang::parse::{parse_expression, parse_program};
use std::path::PathBuf;
fn get_base_path() -> Result<PathBuf, std::io::Error> {
let mut cwd = std::env::current_dir()?;
let mut path = PathBuf::from(file!());
cwd.pop();
path.pop();
path.push("cases");
Ok(cwd.join(path))
}
fn iterate_files() -> Result<Vec<PathBuf>, std::io::Error> {
let base = get_base_path()?;
let mut files = Vec::with_capacity(10);
for file in std::fs::read_dir(base.display().to_string())? {
let entry = file?;
files.push(entry.path());
}
Ok(files)
}
fn read_and_parse(path: PathBuf) -> Result<(), std::io::Error> {
let file = std::fs::read_to_string(path)?;
parse_program(file.as_str()).expect("Failed to parse");
Ok(())
}
#[test]
fn parse_all_programs() -> Result<(), std::io::Error> {
for file in iterate_files()?.into_iter() {
read_and_parse(file)?;
}
Ok(())
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment