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

Add Accessor AST node, support indexing operations in grammar, support print statements

parent 23dfab80
No related branches found
No related tags found
No related merge requests found
Pipeline #511 failed with stages
in 39 seconds
......@@ -191,6 +191,7 @@ pub enum ValueExpression {
FunctionCall(FunctionCall),
DeclareFunction(DeclareFunction),
Typeof(TypeofValue),
Accessor(AccessorPath),
}
impl ValueExpression {
......@@ -455,7 +456,11 @@ impl Display for LiteralNode {
#[derive(Clone)]
#[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 Assignment {
pub ident: Identifier,
pub value: Box<ValueExpression>,
......@@ -475,7 +480,11 @@ pub enum DeclareIdent {
#[derive(Clone)]
#[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 DeclareFunction {
pub ident: Identifier,
pub params: Vec<DeclareIdent>,
......@@ -484,12 +493,40 @@ pub struct DeclareFunction {
#[derive(Clone)]
#[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 FunctionCall {
pub name: Identifier,
pub params: ParameterList,
}
#[derive(Clone)]
#[cfg_attr(any(feature = "debug-ast", test), derive(Debug))]
#[cfg_attr(
feature = "serde",
derive(serde::Serialize, serde::Deserialize),
serde(tag = "node_type", content = "node_value", rename_all = "snake_case")
)]
pub enum AccessorType {
Identifier(Identifier),
FunctionCall(FunctionCall),
}
#[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 AccessorPath {
pub initialiser: Box<ValueExpression>,
pub path: Vec<AccessorType>,
}
macro_rules! impl_into_void_expr {
($($type: ty => $variant: expr),+) => {
$(
......@@ -523,7 +560,8 @@ impl_into_value_expr!(
Identifier => ValueExpression::Identifier,
FunctionCall => ValueExpression::FunctionCall,
DeclareFunction => ValueExpression::DeclareFunction,
TypeofValue => ValueExpression::Typeof
TypeofValue => ValueExpression::Typeof,
AccessorPath => ValueExpression::Accessor
);
impl_into_void_expr!(
......
......@@ -5,35 +5,74 @@ use crate::runtime::numbers::Number;
grammar<'a>;
pub Program: Program = {
<Expression> => Program(ExpressionList { expressions: vec![<>], is_void: false })
<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
},
}
ParameterList: Vec<ValueExpression> = {
"(" <CommaSeperatedValueExpressionList> ")" => <>,
"(" ")" => Vec::new(),
}
CommaSeperatedValueExpressionList: Vec<ValueExpression> = {
ValueExpression => vec![<>],
<mut ls:CommaSeperatedValueExpressionList> "," <ve:ValueExpression> => {
ls.push(ve);
ls
},
}
pub Expression: Expression = {
<TopExpression> => Expression::Value(<>),
ValueExpression => Expression::Value(<>),
VoidExpression => Expression::Void(<>),
}
ValueExpression: ValueExpression = {
BoolOrExpression => <>,
"let" <Identifier> => DeclareIdent::WithoutValue(<>).into(),
"let" <id:Identifier> "=" <expr:BoolOrExpression> => DeclareIdent::WithValue(
Assignment { ident: id, value :Box::new(expr) }
).into(),
}
TopExpression: ValueExpression = {
<BoolOrExpression> => <>,
VoidExpression: VoidExpression = {
UnaryStatement => <>
}
BoolOrExpression: ValueExpression = {
<BoolAndExpression> => <>,
BoolAndExpression => <>,
<lhs:BoolOrExpression> "||" <rhs:BoolAndExpression> => ValueExpression::bool_or(lhs, rhs),
}
BoolAndExpression: ValueExpression = {
<EqualityExpression> => <>,
EqualityExpression => <>,
<lhs:BoolAndExpression> "&&" <rhs:EqualityExpression> => ValueExpression::bool_and(lhs, rhs),
}
EqualityExpression: ValueExpression = {
<ComparisonExpression> => <>,
ComparisonExpression => <>,
<lhs:EqualityExpression> "==" <rhs:ComparisonExpression> => ValueExpression::equals(lhs, rhs),
<lhs:EqualityExpression> "!=" <rhs:ComparisonExpression> => ValueExpression::not_equals(lhs, rhs),
}
ComparisonExpression: ValueExpression = {
<AdditiveExpression> => <>,
AdditiveExpression => <>,
<lhs:ComparisonExpression> "<" <rhs:AdditiveExpression> => ValueExpression::less_than(lhs, rhs),
<lhs:ComparisonExpression> ">" <rhs:AdditiveExpression> => ValueExpression::greater_than(lhs, rhs),
<lhs:ComparisonExpression> "<=" <rhs:AdditiveExpression> => ValueExpression::greater_than_equal(lhs, rhs),
......@@ -41,35 +80,100 @@ ComparisonExpression: ValueExpression = {
}
AdditiveExpression: ValueExpression = {
<MultiplicativeExpression> => <>,
MultiplicativeExpression => <>,
<lhs:AdditiveExpression> "+" <rhs:MultiplicativeExpression> => ValueExpression::add(lhs, rhs),
<lhs:AdditiveExpression> "-" <rhs:MultiplicativeExpression> => ValueExpression::sub(lhs, rhs),
}
MultiplicativeExpression: ValueExpression = {
<UnaryExpression> => <>,
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),
}
UnaryStatement: VoidExpression = {
"print" <UnaryExpression> => Print { expr: Box::new(<>) }.into(),
}
UnaryExpression: ValueExpression = {
<IndexedExpression> => <>,
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 = {
// Include indexing expression here
// <idx:IndexedExpression> "." <id:Identifier> => Indexed access
<BaseExpression> => <>,
CallExpression => <>,
<init:CallExpression> "." <acc:AccessorChainAnyEnding> => AccessorPath {
initialiser: Box::new(init),
path: acc,
}.into(),
}
CallExpression: ValueExpression = {
BaseExpression => <>,
FunctionCall => <>.into(),
}
BaseExpression: ValueExpression = {
<Literal> => <>.into(),
<Identifier> => <>.into(),
"(" <TopExpression> ")" => GroupedExpression { inner: Box::new(<>) }.into(),
Literal => <>.into(),
Identifier => <>.into(),
Conditional => <>,
"(" <ValueExpression> ")" => GroupedExpression { inner: Box::new(<>) }.into(),
}
AccessorChainAnyEnding: Vec<AccessorType> = {
AccessorChainEndingWithProperty => <>,
FunctionCall => vec![AccessorType::FunctionCall(<>)],
<mut ls:AccessorChainEndingWithProperty> "." <func:FunctionCall> => {
ls.push(AccessorType::FunctionCall(func));
ls
},
}
AccessorChainEndingWithProperty: Vec<AccessorType> = {
Identifier => vec![AccessorType::Identifier(<>)],
<func:FunctionCall> "." <id:Identifier> => vec![
AccessorType::FunctionCall(func),
AccessorType::Identifier(id),
],
<mut first:AccessorChainEndingWithProperty> "." <id:Identifier> => {
first.push(AccessorType::Identifier(id));
first
},
<mut first:AccessorChainEndingWithProperty> "." <func:FunctionCall> "." <id:Identifier> => {
first.push(AccessorType::FunctionCall(func));
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![<>],
<mut ls:GuardedBlockList> "else" <gb:GuardedBlock> => {
ls.push(gb);
ls
},
}
GuardedBlock: GuardedBlock = {
"if" <guard:ValueExpression> <block:ExpressionBlock> => GuardedBlock {
guard: Box::new(guard),
block,
},
}
FunctionCall: FunctionCall = {
<id:Identifier> <params:ParameterList> => FunctionCall {
name: id,
params,
}
}
IdentifierAsAlias: IdentifierAlias = {
......
......@@ -36,9 +36,9 @@ export_grammar_fn!(parse_expression = Expression => ExpressionParser);
#[cfg(test)]
mod grammar_test {
use super::parse_expression;
use crate::error::ForgeResult;
use super::{parse_expression, parse_program};
use crate::parse::ast::Expression;
use crate::parser::ast::Program;
use test_case::test_case;
#[test_case("123" => matches Ok(_) ; "Parse literal number")]
......@@ -47,12 +47,29 @@ mod grammar_test {
#[test_case("true" => matches Ok(_) ; "Parse literal true")]
#[test_case("null" => matches Ok(_) ; "Parse literal null")]
#[test_case("if foo {}" => matches Ok(_) ; "Parse conditional")]
#[test_case("if foo {} else if bar {}" => matches Ok(_) ; "Parse multi-conditional")]
#[test_case("if foo {} else if bar {} else {}" => matches Ok(_) ; "Parse multi-conditional with else")]
#[test_case("2 * 4 - 3" => matches Ok(_) ; "Parse arithmetic")]
#[test_case("10 - 2 * 4 + 3" => matches Ok(_) ; "Parse arithmetic reverse")]
#[test_case("foo.bar" => matches Ok(_) ; "Basic property accessor")]
#[test_case("foo.bar()" => matches Ok(_) ; "function 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("123 + if if foo { bar } else { baz } { 200 } else { 300 }" => matches Ok(_) ; "Parse inline condition")]
fn expression_parsing(prog: &str) -> Result<Expression, String> {
parse_expression(prog).map(|foo| {
dbg!(&foo);
foo
parse_expression(prog).map(|expr| {
dbg!(&expr);
expr
})
}
#[test_case("10 - 2 * 4 + 3; false; 12 + 14" => matches Ok(_) ; "Parse value expr list")]
#[test_case("10 - 2 * 4 + 3; false; 12 + 14;" => matches Ok(_) ; "Parse void expr list")]
#[test_case("10 - 2 * 4 + 3;; false; 12 + 14;;;;;" => matches Ok(_) ; "Infinite semicolons")]
fn program_parsing(prog: &str) -> Result<Program, String> {
parse_program(prog).map(|expr| {
dbg!(&expr);
expr
})
}
}
......@@ -175,6 +175,7 @@ impl Visitor for TreePrinter {
ValueExpression::Identifier(ident) => {
self.write(ident);
}
ValueExpression::Accessor(accessor) => self.write("<ACCESSOR>"),
ValueExpression::FunctionCall(call) => {
self.write(&call.name);
self.write("(");
......
......@@ -206,6 +206,7 @@ impl Visitor for SimpleExecutor {
.get_variable(ident)
.cloned()
.ok_or_else(|| RuntimeError::UndefinedVariable(ident.to_string())),
ValueExpression::Accessor(ident) => Err(RuntimeError::Unsupported("Accessor")),
ValueExpression::FunctionCall(_) => Err(RuntimeError::Unsupported("FunctionCall")),
ValueExpression::DeclareFunction(_) => {
Err(RuntimeError::Unsupported("DeclareFunction"))
......
......@@ -250,6 +250,7 @@ impl_from!(float: f32, f64);
#[cfg(test)]
mod gcse_maths {
use super::Number;
use test_case::test_case;
#[test]
fn eq_ignores_internal() {
......@@ -258,15 +259,12 @@ mod gcse_maths {
assert_eq!(Number::float(123.0), Number::float(123.0));
}
#[test]
fn addition() {
let first = Number::integer(123);
let second = Number::integer(456);
let third = Number::float(789.0);
let fourth = Number::float(123.4);
assert_eq!(first + second, Number::integer(579));
assert_eq!(third + fourth, Number::float(912.4));
#[test_case(Number::integer(123), Number::integer(456) => Number::integer(579))]
#[test_case(Number::float(123.0), Number::integer(456) => Number::float(579.0))]
#[test_case(Number::float(123.0), Number::float(456.0) => Number::float(579.0))]
#[test_case(Number::integer(123), Number::float(456.5) => Number::float(579.5))]
fn addition(a: Number, b: Number) -> Number {
a + b
}
#[test]
......
......@@ -102,6 +102,7 @@ impl Visitor for ChunkBuilder {
Err(VmError::chunk_parser("Unsupported expression"))
}
ValueExpression::Identifier(_) => Err(VmError::chunk_parser("Unsupported expression")),
ValueExpression::Accessor(_) => Err(VmError::chunk_parser("Unsupported expression")),
ValueExpression::FunctionCall(_) => {
Err(VmError::chunk_parser("Unsupported expression"))
}
......
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