1
0

Continue syntax overhaul

This commit is contained in:
Jeff 2023-10-06 07:55:14 -04:00
parent 6bab3db5e5
commit a691b1fa34
4 changed files with 166 additions and 51 deletions

View File

@ -9,14 +9,14 @@ use std::{fmt, io, time::SystemTimeError};
pub type Result<T> = std::result::Result<T, Error>; pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug, Clone, PartialEq)] #[derive(Clone, PartialEq)]
#[non_exhaustive] #[non_exhaustive]
pub enum Error { pub enum Error {
UnexpectedSyntax { UnexpectedSyntax {
expected: &'static str, expected: &'static str,
actual: &'static str, actual: &'static str,
location: tree_sitter::Point, location: tree_sitter::Point,
surrounding_text: String, relevant_source: String,
}, },
ExpectedFieldName, ExpectedFieldName,
@ -390,6 +390,12 @@ pub fn expect_number_or_string(actual: &Value) -> Result<()> {
impl std::error::Error for Error {} impl std::error::Error for Error {}
impl fmt::Debug for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{self}")
}
}
impl fmt::Display for Error { impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use Error::*; use Error::*;
@ -531,7 +537,7 @@ impl fmt::Display for Error {
expected, expected,
actual, actual,
location, location,
surrounding_text, relevant_source: surrounding_text,
} => write!( } => write!(
f, f,
"Unexpected syntax at {location}. Expected {expected}, but found {actual}. "Unexpected syntax at {location}. Expected {expected}, but found {actual}.

View File

@ -150,7 +150,7 @@ impl EvaluatorTree for Item {
expected: "comment or statement", expected: "comment or statement",
actual: child.kind(), actual: child.kind(),
location: child.start_position(), location: child.start_position(),
surrounding_text: source[node.byte_range()].to_string(), relevant_source: source[node.byte_range()].to_string(),
}) })
} }
} }
@ -166,43 +166,46 @@ impl EvaluatorTree for Item {
/// Abstract representation of a statement. /// Abstract representation of a statement.
/// ///
/// Items are either comments, which do nothing, or statements, which can be run /// Items are either comments, which do nothing, or statements, which can be run
/// to produce a single value or interact with a context by creating or /// to produce a single value or interact with their context.
/// referencing variables.
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)] #[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
pub enum Statement { pub enum Statement {
Expression(Expression),
ControlFlow(Box<ControlFlow>),
Assignment(Box<Assignment>), Assignment(Box<Assignment>),
Expression(Expression),
IfElse(Box<IfElse>),
Match(Match),
} }
impl EvaluatorTree for Statement { impl EvaluatorTree for Statement {
fn from_syntax_node(node: Node, source: &str) -> Result<Self> { fn from_syntax_node(node: Node, source: &str) -> Result<Self> {
debug_assert_eq!("statement", node.kind());
let child = node.child(0).unwrap(); let child = node.child(0).unwrap();
match child.kind() { match child.kind() {
"expression" => Ok(Self::Expression(Expression::from_syntax_node(
child, source,
)?)),
"control_flow" => Ok(Statement::ControlFlow(Box::new(
ControlFlow::from_syntax_node(child, source)?,
))),
"assignment" => Ok(Statement::Assignment(Box::new( "assignment" => Ok(Statement::Assignment(Box::new(
Assignment::from_syntax_node(child, source)?, Assignment::from_syntax_node(child, source)?,
))), ))),
"expression" => Ok(Self::Expression(Expression::from_syntax_node(
child, source,
)?)),
"if_else" => Ok(Statement::IfElse(Box::new(IfElse::from_syntax_node(
child, source,
)?))),
_ => Err(Error::UnexpectedSyntax { _ => Err(Error::UnexpectedSyntax {
expected: "expression", expected: "assignment, expression or if...else",
actual: child.kind(), actual: child.kind(),
location: child.start_position(), location: child.start_position(),
surrounding_text: source[node.byte_range()].to_string(), relevant_source: source[node.byte_range()].to_string(),
}), }),
} }
} }
fn run(&self, context: &mut VariableMap) -> Result<Value> { fn run(&self, context: &mut VariableMap) -> Result<Value> {
match self { match self {
Statement::Expression(expression) => expression.run(context),
Statement::ControlFlow(control_flow) => control_flow.run(context),
Statement::Assignment(assignment) => assignment.run(context), Statement::Assignment(assignment) => assignment.run(context),
Statement::Expression(expression) => expression.run(context),
Statement::IfElse(if_else) => if_else.run(context),
Statement::Match(r#match) => r#match.run(context),
} }
} }
} }
@ -212,6 +215,7 @@ pub enum Expression {
Identifier(Identifier), Identifier(Identifier),
Value(Value), Value(Value),
Math(Box<Math>), Math(Box<Math>),
Logic(Box<Logic>),
FunctionCall(FunctionCall), FunctionCall(FunctionCall),
} }
@ -220,19 +224,21 @@ impl EvaluatorTree for Expression {
let child = node.child(0).unwrap(); let child = node.child(0).unwrap();
let expression = match child.kind() { let expression = match child.kind() {
"identifier" => Self::Identifier(Identifier::from_syntax_node(child, source)?),
"value" => Expression::Value(Value::from_syntax_node(child, source)?), "value" => Expression::Value(Value::from_syntax_node(child, source)?),
"identifier" => Self::Identifier(Identifier::from_syntax_node(child, source)?),
"math" => Expression::Math(Box::new(Math::from_syntax_node(child, source)?)), "math" => Expression::Math(Box::new(Math::from_syntax_node(child, source)?)),
"logic" => Expression::Logic(Box::new(Logic::from_syntax_node(child, source)?)),
"function_call" => { "function_call" => {
Expression::FunctionCall(FunctionCall::from_syntax_node(child, source)?) Expression::FunctionCall(FunctionCall::from_syntax_node(child, source)?)
} }
_ => return Err(Error::UnexpectedSyntax { _ => {
expected: return Err(Error::UnexpectedSyntax {
"identifier, operation, control_flow, assignment, math, function_call or value", expected: "value, identifier, math or function_call",
actual: child.kind(), actual: child.kind(),
location: child.start_position(), location: child.start_position(),
surrounding_text: source[node.byte_range()].to_string(), relevant_source: source[node.byte_range()].to_string(),
}), })
}
}; };
Ok(expression) Ok(expression)
@ -244,6 +250,7 @@ impl EvaluatorTree for Expression {
Expression::Identifier(identifier) => identifier.run(context), Expression::Identifier(identifier) => identifier.run(context),
Expression::Math(math) => math.run(context), Expression::Math(math) => math.run(context),
Expression::FunctionCall(function_call) => function_call.run(context), Expression::FunctionCall(function_call) => function_call.run(context),
Expression::Logic(logic) => logic.run(context),
} }
} }
} }
@ -280,13 +287,13 @@ impl EvaluatorTree for Identifier {
} }
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)] #[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
pub struct ControlFlow { pub struct IfElse {
if_expression: Expression, if_expression: Expression,
then_statement: Statement, then_statement: Statement,
else_statement: Option<Statement>, else_statement: Option<Statement>,
} }
impl EvaluatorTree for ControlFlow { impl EvaluatorTree for IfElse {
fn from_syntax_node(node: Node, source: &str) -> Result<Self> { fn from_syntax_node(node: Node, source: &str) -> Result<Self> {
let if_node = node.child(1).unwrap(); let if_node = node.child(1).unwrap();
let if_expression = Expression::from_syntax_node(if_node, source)?; let if_expression = Expression::from_syntax_node(if_node, source)?;
@ -301,7 +308,9 @@ impl EvaluatorTree for ControlFlow {
None None
}; };
Ok(ControlFlow { println!("{if_node:?} {then_node:?} {else_node:?}");
Ok(IfElse {
if_expression, if_expression,
then_statement, then_statement,
else_statement, else_statement,
@ -321,10 +330,23 @@ impl EvaluatorTree for ControlFlow {
} }
} }
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
pub struct Match {}
impl EvaluatorTree for Match {
fn from_syntax_node(node: Node, source: &str) -> Result<Self> {
todo!()
}
fn run(&self, context: &mut VariableMap) -> Result<Value> {
todo!()
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)] #[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
pub struct Assignment { pub struct Assignment {
identifier: Identifier, identifier: Identifier,
expression: Expression, statement: Statement,
} }
impl EvaluatorTree for Assignment { impl EvaluatorTree for Assignment {
@ -332,18 +354,18 @@ impl EvaluatorTree for Assignment {
let identifier_node = node.child(0).unwrap(); let identifier_node = node.child(0).unwrap();
let identifier = Identifier::from_syntax_node(identifier_node, source)?; let identifier = Identifier::from_syntax_node(identifier_node, source)?;
let expression_node = node.child(2).unwrap(); let statement_node = node.child(2).unwrap();
let expression = Expression::from_syntax_node(expression_node, source)?; let statement = Statement::from_syntax_node(statement_node, source)?;
Ok(Assignment { Ok(Assignment {
identifier, identifier,
expression, statement,
}) })
} }
fn run(&self, context: &mut VariableMap) -> Result<Value> { fn run(&self, context: &mut VariableMap) -> Result<Value> {
let key = self.identifier.clone().take_inner(); let key = self.identifier.clone().take_inner();
let value = self.expression.run(context)?; let value = self.statement.run(context)?;
context.set_value(key, value)?; context.set_value(key, value)?;
@ -375,7 +397,7 @@ impl EvaluatorTree for Math {
expected: "+, -, *, / or %", expected: "+, -, *, / or %",
actual: operator_node.kind(), actual: operator_node.kind(),
location: operator_node.start_position(), location: operator_node.start_position(),
surrounding_text: source[operator_node.byte_range()].to_string(), relevant_source: source[operator_node.byte_range()].to_string(),
}) })
} }
}; };
@ -391,19 +413,33 @@ impl EvaluatorTree for Math {
} }
fn run(&self, context: &mut VariableMap) -> Result<Value> { fn run(&self, context: &mut VariableMap) -> Result<Value> {
let left_value = self.left.run(context)?.as_number()?; match self.operator {
let right_value = self.right.run(context)?.as_number()?; MathOperator::Add | MathOperator::Subtract | MathOperator::Multiply => {
let outcome = match self.operator { let left_value = self.left.run(context)?.as_int()?;
let right_value = self.right.run(context)?.as_int()?;
let outcome = match &self.operator {
MathOperator::Add => left_value + right_value, MathOperator::Add => left_value + right_value,
MathOperator::Subtract => left_value - right_value, MathOperator::Subtract => left_value - right_value,
MathOperator::Multiply => left_value * right_value, MathOperator::Multiply => left_value * right_value,
_ => panic!("Unreachable"),
};
Ok(Value::Integer(outcome))
}
MathOperator::Divide | MathOperator::Modulo => {
let left_value = self.left.run(context)?.as_number()?;
let right_value = self.right.run(context)?.as_number()?;
let outcome = match self.operator {
MathOperator::Divide => left_value / right_value, MathOperator::Divide => left_value / right_value,
MathOperator::Modulo => left_value % right_value, MathOperator::Modulo => left_value % right_value,
_ => panic!("Unreachable"),
}; };
Ok(Value::Float(outcome)) Ok(Value::Float(outcome))
} }
} }
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)] #[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
pub enum MathOperator { pub enum MathOperator {
@ -414,6 +450,63 @@ pub enum MathOperator {
Modulo, Modulo,
} }
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
pub struct Logic {
left: Expression,
operator: LogicOperator,
right: Expression,
}
impl EvaluatorTree for Logic {
fn from_syntax_node(node: Node, source: &str) -> Result<Self> {
let left_node = node.child(0).unwrap();
let left = Expression::from_syntax_node(left_node, source)?;
let operator_node = node.child(1).unwrap().child(0).unwrap();
let operator = match operator_node.kind() {
"==" => LogicOperator::Equal,
"&&" => LogicOperator::And,
"||" => LogicOperator::Or,
_ => {
return Err(Error::UnexpectedSyntax {
expected: "==, && or ||",
actual: operator_node.kind(),
location: operator_node.start_position(),
relevant_source: source[operator_node.byte_range()].to_string(),
})
}
};
let right_node = node.child(2).unwrap();
let right = Expression::from_syntax_node(right_node, source)?;
Ok(Logic {
left,
operator,
right,
})
}
fn run(&self, context: &mut VariableMap) -> Result<Value> {
let left_value = self.left.run(context)?;
let right_value = self.right.run(context)?;
let outcome = match self.operator {
LogicOperator::Equal => left_value == right_value,
LogicOperator::And => left_value.as_boolean()? && right_value.as_boolean()?,
LogicOperator::Or => left_value.as_boolean()? || right_value.as_boolean()?,
};
Ok(Value::Boolean(outcome))
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
pub enum LogicOperator {
Equal,
And,
Or,
}
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)] #[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
pub struct FunctionCall { pub struct FunctionCall {
identifier: Identifier, identifier: Identifier,
@ -574,13 +667,13 @@ mod tests {
} }
#[test] #[test]
fn evaluate_if_else_if_then_else() { fn evaluate_if_else_else_if_else() {
assert_eq!( assert_eq!(
evaluate( evaluate(
" "
if false if false
then 'no' then 'no'
else if 1 + 1 = 3 else if 1 + 1 == 3
then 'nope' then 'nope'
else else
'ok' 'ok'
@ -588,9 +681,25 @@ mod tests {
), ),
vec![Ok(Value::String("ok".to_string()))] vec![Ok(Value::String("ok".to_string()))]
); );
}
#[test]
fn evaluate_if_else_else_if_else_if_else_if_else() {
assert_eq!( assert_eq!(
evaluate("if true then 1.0 else 42.0"), evaluate(
vec![Ok(Value::Float(1.0))] "
if false
then 'no'
else if 1 + 1 == 1
then 'nope'
else if 9 / 2 == 4
then 'nope'
else if 'foo' == 'bar'
then 'nope'
else 'ok'
"
),
vec![Ok(Value::String("ok".to_string()))]
); );
} }

View File

@ -183,7 +183,7 @@ impl Value {
expected: "string, integer, float, boolean, list, table, map, function or empty", expected: "string, integer, float, boolean, list, table, map, function or empty",
actual: child.kind(), actual: child.kind(),
location: child.start_position(), location: child.start_position(),
surrounding_text: source[child.byte_range()].to_string(), relevant_source: source[child.byte_range()].to_string(),
}), }),
} }
} }

@ -1 +1 @@
Subproject commit 4ddc65645541293ca2690c874b0ac8eb62f7e5e4 Subproject commit 405a2dc86c75617daa567781469708b0aaf0a0c0