diff --git a/forge-script-lang/src/parser/forge_grammar.lalrpop b/forge-script-lang/src/parser/forge_grammar.lalrpop index 0d20fe9758741a8f785d38a5a383106d53ffd044..72b701614a56bf95a390657ac79cefd941c4948f 100644 --- a/forge-script-lang/src/parser/forge_grammar.lalrpop +++ b/forge-script-lang/src/parser/forge_grammar.lalrpop @@ -6,46 +6,19 @@ grammar<'a>; pub Program: Program = { <ExpressionList> => Program(<>), -} - -ExpressionBlock: ExpressionList = { - "{" "}" => ExpressionList::empty(), - "{" <ExpressionList> "}" => <>, -} - -ExpressionList: ExpressionList = { - <Expression> => ExpressionList::production(vec![<>]), - <mut list:ExpressionList> ";" <expr:Expression> => { - list.expressions.push(expr); - list - }, - <mut opt:ExpressionList> ";" => { - opt.is_void = true; - opt - }, -} - -FunctionDeclParamList = CommaSeperatedList<FunctionDeclParam>; - -FunctionDeclParam: DeclareIdent = { - Identifier => DeclareIdent::WithoutValue(<>), - <id:Identifier> "=" <def:ValueExpression> => DeclareIdent::WithValue(Assignment { - ident: id, - value: Box::new(def), - }), -} +}; ParameterList: Vec<ValueExpression> = { "(" <CommaSeperatedValueExpressionList> ")" => <>, "(" ")" => Vec::new(), -} +}; CommaSeperatedValueExpressionList = CommaSeperatedList<ValueExpression>; pub Expression: Expression = { ValueExpression => Expression::Value(<>), VoidExpression => Expression::Void(<>), -} +}; ValueExpression: ValueExpression = { FirstClassDeclareExpression => <>, @@ -53,32 +26,34 @@ ValueExpression: ValueExpression = { "let" <id:Identifier> "=" <expr:FirstClassDeclareExpression> => DeclareIdent::WithValue( Assignment { ident: id, value :Box::new(expr) } ).into(), -} +}; VoidExpression: VoidExpression = { - UnaryStatement => <> -} + UnaryStatement => <>, + IOStatement => <>, + ConditionalLoop => <>.into(), +}; FirstClassDeclareExpression: ValueExpression = { BoolOrExpression => <>, DeclareFunction => <>.into(), -} +}; BoolOrExpression: ValueExpression = { BoolAndExpression => <>, <lhs:BoolOrExpression> "||" <rhs:BoolAndExpression> => ValueExpression::bool_or(lhs, rhs), -} +}; BoolAndExpression: ValueExpression = { EqualityExpression => <>, <lhs:BoolAndExpression> "&&" <rhs:EqualityExpression> => ValueExpression::bool_and(lhs, rhs), -} +}; EqualityExpression: ValueExpression = { ComparisonExpression => <>, <lhs:EqualityExpression> "==" <rhs:ComparisonExpression> => ValueExpression::equals(lhs, rhs), <lhs:EqualityExpression> "!=" <rhs:ComparisonExpression> => ValueExpression::not_equals(lhs, rhs), -} +}; ComparisonExpression: ValueExpression = { AdditiveExpression => <>, @@ -86,32 +61,37 @@ ComparisonExpression: ValueExpression = { <lhs:ComparisonExpression> ">" <rhs:AdditiveExpression> => ValueExpression::greater_than(lhs, rhs), <lhs:ComparisonExpression> "<=" <rhs:AdditiveExpression> => ValueExpression::greater_than_equal(lhs, rhs), <lhs:ComparisonExpression> ">=" <rhs:AdditiveExpression> => ValueExpression::greater_than_equal(lhs, rhs), -} +}; AdditiveExpression: ValueExpression = { MultiplicativeExpression => <>, <lhs:AdditiveExpression> "+" <rhs:MultiplicativeExpression> => ValueExpression::add(lhs, rhs), <lhs:AdditiveExpression> "-" <rhs:MultiplicativeExpression> => ValueExpression::sub(lhs, rhs), -} +}; MultiplicativeExpression: ValueExpression = { UnaryExpression => <>, <lhs:MultiplicativeExpression> "*" <rhs:UnaryExpression> => ValueExpression::mul(lhs, rhs), <lhs:MultiplicativeExpression> "/" <rhs:UnaryExpression> => ValueExpression::div(lhs, rhs), <lhs:MultiplicativeExpression> "%" <rhs:UnaryExpression> => ValueExpression::modulo(lhs, rhs), -} +}; + +IOStatement: VoidExpression = { + "import" "{" <items:CommaSeperatedList<IdentifierNode>> "}" "from" <source:StringValue> => Import { items, source }.into(), + "export" "{" <items:CommaSeperatedList<IdentifierNode>> "}" => Export { items }.into(), +}; UnaryStatement: VoidExpression = { "print" <FirstClassDeclareExpression> => Print { expr: Box::new(<>) }.into(), "return" <FirstClassDeclareExpression> => Return { expr: Box::new(<>) }.into(), -} +}; UnaryExpression: ValueExpression = { IndexedExpression => <>, "typeof" <UnaryExpression> => TypeofValue(Box::new(<>)).into(), "!" <UnaryExpression> => ValueExpression::Unary { operand: Box::new(<>), operator: UnaryOp::Not }, "-" <UnaryExpression> => ValueExpression::Unary { operand: Box::new(<>), operator: UnaryOp::Negate }, -} +}; IndexedExpression: ValueExpression = { CallExpression => <>, @@ -119,19 +99,36 @@ IndexedExpression: ValueExpression = { initialiser: Box::new(init), path: acc, }.into(), -} +}; CallExpression: ValueExpression = { BaseExpression => <>, FunctionCall => <>.into(), -} +}; BaseExpression: ValueExpression = { Literal => <>.into(), Identifier => <>.into(), Conditional => <>, "(" <ValueExpression> ")" => GroupedExpression { inner: Box::new(<>) }.into(), -} +}; + +ExpressionBlock: ExpressionList = { + "{" <ExpressionList> "}" => <>, + "{" "}" => ExpressionList::empty(), +}; + +ExpressionList: ExpressionList = { + <Expression> => ExpressionList::production(vec![<>]), + <mut list:ExpressionList> ";" <expr:Expression> => { + list.expressions.push(expr); + list + }, + <mut opt:ExpressionList> ";" => { + opt.is_void = true; + opt + }, +}; DeclareFunction: DeclareFunction = { "fn" <ident:Identifier> "(" <params:FunctionDeclParamList?> ")" <body:ExpressionBlock> => DeclareFunction { @@ -139,7 +136,17 @@ DeclareFunction: DeclareFunction = { params: params.unwrap_or_default(), body, } -} +}; + +FunctionDeclParamList = CommaSeperatedList<FunctionDeclParam>; + +FunctionDeclParam: DeclareIdent = { + Identifier => DeclareIdent::WithoutValue(<>), + <id:Identifier> "=" <def:ValueExpression> => DeclareIdent::WithValue(Assignment { + ident: id, + value: Box::new(def), + }), +}; AccessorChainAnyEnding: Vec<AccessorType> = { AccessorChainEndingWithProperty => <>, @@ -148,7 +155,7 @@ AccessorChainAnyEnding: Vec<AccessorType> = { ls.push(AccessorType::FunctionCall(func)); ls }, -} +}; AccessorChainEndingWithProperty: Vec<AccessorType> = { Identifier => vec![AccessorType::Identifier(<>)], @@ -165,12 +172,12 @@ AccessorChainEndingWithProperty: Vec<AccessorType> = { first.push(AccessorType::Identifier(id)); first }, -} +}; Conditional: ValueExpression = { GuardedBlockList => Conditional { blocks: <>, fallback: None }.into(), <ls:GuardedBlockList> "else" <fb:ExpressionBlock> => Conditional { blocks: ls, fallback: Some(fb) }.into(), -} +}; GuardedBlockList: Vec<GuardedBlock> = { GuardedBlock => vec![<>], @@ -178,24 +185,46 @@ GuardedBlockList: Vec<GuardedBlock> = { ls.push(gb); ls }, -} +}; GuardedBlock: GuardedBlock = { "if" <guard:ValueExpression> <block:ExpressionBlock> => GuardedBlock { guard: Box::new(guard), block, }, -} +}; + +ConditionalLoop: ConditionalLoop = { + "while" <guard: ValueExpression> <block:ExpressionBlock> => ConditionalLoop { + block: GuardedBlock { + guard: Box::new(guard), + block, + }, + fallback: None, + }, + "while" <guard: ValueExpression> <block:ExpressionBlock> "finally" <fallback:ExpressionBlock> => ConditionalLoop { + block: GuardedBlock { + guard: Box::new(guard), + block, + }, + fallback: Some(fallback), + }, +}; FunctionCall: FunctionCall = { <id:Identifier> <params:ParameterList> => FunctionCall { name: id, params, } -} +}; + +IdentifierNode: IdentifierNode = { + Identifier => IdentifierNode::Direct(<>), + IdentifierAsAlias => IdentifierNode::Alias(<>), +}; IdentifierAsAlias: IdentifierAlias = { - <base:Identifier> "as" <alias:Identifier> => IdentifierAlias(base.0, alias.0), + <base:"identifier"> "as" <alias:"identifier"> => IdentifierAlias(base, alias), }; Identifier: Identifier = { @@ -222,7 +251,7 @@ CommaSeperatedList<Type>: Vec<Type> = { ls.push(ty); ls } -} +}; extern { type Location = usize; diff --git a/forge-script-lang/tests/cases/conditionals.fs b/forge-script-lang/tests/cases/conditionals.fs index b43b7998414be1adbd0f8a465f993c0af97f5604..2a57b2f144bb1624e346ed65deecc71392a4bd30 100644 --- a/forge-script-lang/tests/cases/conditionals.fs +++ b/forge-script-lang/tests/cases/conditionals.fs @@ -1,5 +1,5 @@ -import { random_float, random_int, random_bool } from "@runtime:random" -import { truncate } from "@runtime:maths" +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(); @@ -7,4 +7,12 @@ let my_var = if if 1 + 2 > 4 { var_a } else { var_b } { truncate(random_float() * 10) } else { random_int(100, 200) +}; + +export { my_var }; + +while some_expression { + print "foo"; +} finally { + print "bar"; }; \ No newline at end of file