Implement structure parsing
This commit is contained in:
parent
16d443d8a6
commit
bcc89f2c7d
@ -65,7 +65,7 @@ impl AbstractTree for FunctionCall {
|
||||
} else {
|
||||
return Err(RuntimeError::ValidationFailure(
|
||||
ValidationError::ExpectedFunction {
|
||||
actual: value.r#type(),
|
||||
actual: value.r#type(context)?,
|
||||
position: self.function.position,
|
||||
},
|
||||
));
|
||||
|
@ -63,8 +63,8 @@ impl AbstractTree for IfElse {
|
||||
}
|
||||
}
|
||||
|
||||
fn run(self, _context: &Context) -> Result<Action, RuntimeError> {
|
||||
let action = self.if_expression.node.run(_context)?;
|
||||
fn run(self, context: &Context) -> Result<Action, RuntimeError> {
|
||||
let action = self.if_expression.node.run(context)?;
|
||||
let value = if let Action::Return(value) = action {
|
||||
value
|
||||
} else {
|
||||
@ -75,16 +75,16 @@ impl AbstractTree for IfElse {
|
||||
|
||||
if let ValueInner::Boolean(if_boolean) = value.inner().as_ref() {
|
||||
if *if_boolean {
|
||||
self.if_block.run(_context)
|
||||
self.if_block.run(context)
|
||||
} else if let Some(else_statement) = self.else_block {
|
||||
else_statement.run(_context)
|
||||
else_statement.run(context)
|
||||
} else {
|
||||
Ok(Action::None)
|
||||
}
|
||||
} else {
|
||||
Err(RuntimeError::ValidationFailure(
|
||||
ValidationError::ExpectedBoolean {
|
||||
actual: value.r#type(),
|
||||
actual: value.r#type(context)?,
|
||||
position: self.if_expression.position,
|
||||
},
|
||||
))
|
||||
|
@ -68,8 +68,8 @@ impl AbstractTree for ListIndex {
|
||||
}
|
||||
}
|
||||
|
||||
fn run(self, _context: &Context) -> Result<Action, RuntimeError> {
|
||||
let left_action = self.left.node.run(_context)?;
|
||||
fn run(self, context: &Context) -> Result<Action, RuntimeError> {
|
||||
let left_action = self.left.node.run(context)?;
|
||||
let left_value = if let Action::Return(value) = left_action {
|
||||
value
|
||||
} else {
|
||||
@ -77,7 +77,7 @@ impl AbstractTree for ListIndex {
|
||||
ValidationError::InterpreterExpectedReturn(self.left.position),
|
||||
));
|
||||
};
|
||||
let right_action = self.right.node.run(_context)?;
|
||||
let right_action = self.right.node.run(context)?;
|
||||
let right_value = if let Action::Return(value) = right_action {
|
||||
value
|
||||
} else {
|
||||
@ -97,9 +97,9 @@ impl AbstractTree for ListIndex {
|
||||
} else {
|
||||
Err(RuntimeError::ValidationFailure(
|
||||
ValidationError::CannotIndexWith {
|
||||
collection_type: left_value.r#type(),
|
||||
collection_type: left_value.r#type(context)?,
|
||||
collection_position: self.left.position,
|
||||
index_type: right_value.r#type(),
|
||||
index_type: right_value.r#type(context)?,
|
||||
index_position: self.right.position,
|
||||
},
|
||||
))
|
||||
|
@ -83,10 +83,10 @@ impl AbstractTree for Logic {
|
||||
}
|
||||
}
|
||||
|
||||
fn run(self, _context: &Context) -> Result<Action, RuntimeError> {
|
||||
fn run(self, context: &Context) -> Result<Action, RuntimeError> {
|
||||
let run_and_expect_value =
|
||||
|expression: WithPosition<Expression>| -> Result<Value, RuntimeError> {
|
||||
let action = expression.node.run(_context)?;
|
||||
let action = expression.node.run(context)?;
|
||||
let value = if let Action::Return(value) = action {
|
||||
value
|
||||
} else {
|
||||
@ -100,7 +100,7 @@ impl AbstractTree for Logic {
|
||||
|
||||
let run_and_expect_boolean =
|
||||
|expression: WithPosition<Expression>| -> Result<bool, RuntimeError> {
|
||||
let action = expression.node.run(_context)?;
|
||||
let action = expression.node.run(context)?;
|
||||
let value = if let Action::Return(value) = action {
|
||||
value
|
||||
} else {
|
||||
@ -114,7 +114,7 @@ impl AbstractTree for Logic {
|
||||
} else {
|
||||
return Err(RuntimeError::ValidationFailure(
|
||||
ValidationError::ExpectedBoolean {
|
||||
actual: value.r#type(),
|
||||
actual: value.r#type(context)?,
|
||||
position: expression.position,
|
||||
},
|
||||
));
|
||||
|
@ -19,15 +19,15 @@ impl MapIndex {
|
||||
}
|
||||
|
||||
impl AbstractTree for MapIndex {
|
||||
fn expected_type(&self, _context: &Context) -> Result<Type, ValidationError> {
|
||||
let left_type = self.left.node.expected_type(_context)?;
|
||||
fn expected_type(&self, context: &Context) -> Result<Type, ValidationError> {
|
||||
let left_type = self.left.node.expected_type(context)?;
|
||||
|
||||
if let (
|
||||
Expression::Identifier(collection_identifier),
|
||||
Expression::Identifier(index_identifier),
|
||||
) = (&self.left.node, &self.right.node)
|
||||
{
|
||||
let collection = if let Some(collection) = _context.get_value(collection_identifier)? {
|
||||
let collection = if let Some(collection) = context.get_value(collection_identifier)? {
|
||||
collection
|
||||
} else {
|
||||
return Err(ValidationError::VariableNotFound(
|
||||
@ -37,7 +37,7 @@ impl AbstractTree for MapIndex {
|
||||
|
||||
if let ValueInner::Map(map) = collection.inner().as_ref() {
|
||||
return if let Some(value) = map.get(index_identifier) {
|
||||
Ok(value.r#type())
|
||||
Ok(value.r#type(context)?)
|
||||
} else {
|
||||
Err(ValidationError::PropertyNotFound {
|
||||
identifier: index_identifier.clone(),
|
||||
@ -56,9 +56,9 @@ impl AbstractTree for MapIndex {
|
||||
.find_map(|(property, type_option, expression)| {
|
||||
if property == identifier {
|
||||
if let Some(r#type) = type_option {
|
||||
Some(r#type.node.expected_type(_context))
|
||||
Some(r#type.node.expected_type(context))
|
||||
} else {
|
||||
Some(expression.node.expected_type(_context))
|
||||
Some(expression.node.expected_type(context))
|
||||
}
|
||||
} else {
|
||||
None
|
||||
@ -74,7 +74,7 @@ impl AbstractTree for MapIndex {
|
||||
Err(ValidationError::CannotIndexWith {
|
||||
collection_type: left_type,
|
||||
collection_position: self.left.position,
|
||||
index_type: self.right.node.expected_type(_context)?,
|
||||
index_type: self.right.node.expected_type(context)?,
|
||||
index_position: self.right.position,
|
||||
})
|
||||
}
|
||||
@ -100,8 +100,8 @@ impl AbstractTree for MapIndex {
|
||||
}
|
||||
}
|
||||
|
||||
fn run(self, _context: &Context) -> Result<Action, RuntimeError> {
|
||||
let action = self.left.node.run(_context)?;
|
||||
fn run(self, context: &Context) -> Result<Action, RuntimeError> {
|
||||
let action = self.left.node.run(context)?;
|
||||
let collection = if let Action::Return(value) = action {
|
||||
value
|
||||
} else {
|
||||
@ -122,9 +122,9 @@ impl AbstractTree for MapIndex {
|
||||
} else {
|
||||
Err(RuntimeError::ValidationFailure(
|
||||
ValidationError::CannotIndexWith {
|
||||
collection_type: collection.r#type(),
|
||||
collection_type: collection.r#type(context)?,
|
||||
collection_position: self.left.position,
|
||||
index_type: self.right.node.expected_type(_context)?,
|
||||
index_type: self.right.node.expected_type(context)?,
|
||||
index_position: self.right.position,
|
||||
},
|
||||
))
|
||||
|
@ -25,7 +25,7 @@ pub enum ValueNode {
|
||||
String(String),
|
||||
Structure {
|
||||
name: Identifier,
|
||||
fields: Vec<(Identifier, Type)>,
|
||||
fields: Vec<(Identifier, WithPosition<Expression>)>,
|
||||
},
|
||||
Function {
|
||||
parameters: Vec<(Identifier, WithPosition<Type>)>,
|
||||
@ -244,14 +244,6 @@ impl Ord for ValueNode {
|
||||
fields: right_fields,
|
||||
},
|
||||
) => todo!(),
|
||||
(
|
||||
Structure { name, fields },
|
||||
Function {
|
||||
parameters,
|
||||
return_type,
|
||||
body,
|
||||
},
|
||||
) => todo!(),
|
||||
(Structure { name, fields }, _) => todo!(),
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ use std::{
|
||||
|
||||
use crate::{
|
||||
abstract_tree::{Identifier, Type},
|
||||
error::RwLockPoisonError,
|
||||
error::{RwLockPoisonError, ValidationError},
|
||||
value::{BUILT_IN_FUNCTIONS, BUILT_IN_MODULES},
|
||||
Value,
|
||||
};
|
||||
@ -72,11 +72,11 @@ impl Context {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_type(&self, identifier: &Identifier) -> Result<Option<Type>, RwLockPoisonError> {
|
||||
pub fn get_type(&self, identifier: &Identifier) -> Result<Option<Type>, ValidationError> {
|
||||
if let Some(value_data) = self.inner.read()?.get(identifier) {
|
||||
let r#type = match value_data {
|
||||
ValueData::Type(r#type) => r#type.clone(),
|
||||
ValueData::Value(value) => value.r#type(),
|
||||
ValueData::Value(value) => value.r#type(self)?,
|
||||
};
|
||||
|
||||
return Ok(Some(r#type.clone()));
|
||||
|
@ -291,6 +291,12 @@ impl From<RwLockPoisonError> for ValidationError {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<PoisonError<T>> for ValidationError {
|
||||
fn from(_: PoisonError<T>) -> Self {
|
||||
ValidationError::RwLockPoison(RwLockPoisonError)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct RwLockPoisonError;
|
||||
|
||||
|
@ -336,7 +336,30 @@ pub fn parser<'src>() -> impl Parser<
|
||||
),
|
||||
));
|
||||
|
||||
let structure_field = identifier
|
||||
.clone()
|
||||
.then_ignore(just(Token::Operator(Operator::Assign)))
|
||||
.then(positioned_expression.clone());
|
||||
|
||||
let structure_instance = identifier
|
||||
.clone()
|
||||
.then(
|
||||
structure_field
|
||||
.separated_by(just(Token::Control(Control::Comma)))
|
||||
.allow_trailing()
|
||||
.collect()
|
||||
.delimited_by(
|
||||
just(Token::Control(Control::CurlyOpen)),
|
||||
just(Token::Control(Control::CurlyClose)),
|
||||
),
|
||||
)
|
||||
.map_with(|(name, fields), state| {
|
||||
Expression::Value(ValueNode::Structure { name, fields })
|
||||
.with_position(state.span())
|
||||
});
|
||||
|
||||
choice((
|
||||
structure_instance,
|
||||
range,
|
||||
logic_math_indexes_and_function_calls,
|
||||
function,
|
||||
@ -417,12 +440,12 @@ pub fn parser<'src>() -> impl Parser<
|
||||
.with_position(state.span())
|
||||
});
|
||||
|
||||
let structure_field = identifier.clone().then(type_specification.clone());
|
||||
let structure_field_definition = identifier.clone().then(type_specification.clone());
|
||||
|
||||
let structure_definition = just(Token::Keyword("struct"))
|
||||
.ignore_then(identifier.clone())
|
||||
.then(
|
||||
structure_field
|
||||
structure_field_definition
|
||||
.separated_by(just(Token::Control(Control::Comma)))
|
||||
.allow_trailing()
|
||||
.collect()
|
||||
@ -458,6 +481,37 @@ mod tests {
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn structure_instance() {
|
||||
assert_eq!(
|
||||
parse(
|
||||
&lex("
|
||||
Foo {
|
||||
bar = 42,
|
||||
baz = 'hiya',
|
||||
}
|
||||
")
|
||||
.unwrap()
|
||||
)
|
||||
.unwrap()[0]
|
||||
.node,
|
||||
Statement::Expression(Expression::Value(ValueNode::Structure {
|
||||
name: Identifier::new("Foo"),
|
||||
fields: vec![
|
||||
(
|
||||
Identifier::new("bar"),
|
||||
Expression::Value(ValueNode::Integer(42)).with_position((57, 59))
|
||||
),
|
||||
(
|
||||
Identifier::new("baz"),
|
||||
Expression::Value(ValueNode::String("hiya".to_string()))
|
||||
.with_position((91, 97))
|
||||
),
|
||||
]
|
||||
}))
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn structure_definition() {
|
||||
assert_eq!(
|
||||
|
66
src/value.rs
66
src/value.rs
@ -15,7 +15,7 @@ use stanza::{
|
||||
};
|
||||
|
||||
use crate::{
|
||||
abstract_tree::{AbstractTree, Action, Block, Identifier, Type, WithPosition},
|
||||
abstract_tree::{AbstractTree, Action, Block, Expression, Identifier, Type, WithPosition},
|
||||
context::Context,
|
||||
error::{RuntimeError, ValidationError},
|
||||
};
|
||||
@ -74,8 +74,8 @@ impl Value {
|
||||
Value(Arc::new(ValueInner::Function(Function::BuiltIn(function))))
|
||||
}
|
||||
|
||||
pub fn r#type(&self) -> Type {
|
||||
match self.0.as_ref() {
|
||||
pub fn r#type(&self, context: &Context) -> Result<Type, ValidationError> {
|
||||
let r#type = match self.0.as_ref() {
|
||||
ValueInner::Boolean(_) => Type::Boolean,
|
||||
ValueInner::Float(_) => Type::Float,
|
||||
ValueInner::Integer(_) => Type::Integer,
|
||||
@ -83,7 +83,7 @@ impl Value {
|
||||
let mut types = Vec::with_capacity(values.len());
|
||||
|
||||
for value in values {
|
||||
types.push(value.r#type());
|
||||
types.push(value.r#type(context)?);
|
||||
}
|
||||
|
||||
Type::ListExact(types)
|
||||
@ -102,7 +102,29 @@ impl Value {
|
||||
},
|
||||
Function::BuiltIn(built_in_function) => built_in_function.r#type(),
|
||||
},
|
||||
ValueInner::Structure {
|
||||
name,
|
||||
fields: expressions,
|
||||
} => {
|
||||
let mut fields = Vec::with_capacity(expressions.len());
|
||||
|
||||
for (identifier, expression) in expressions {
|
||||
let r#type = expression
|
||||
.node
|
||||
.expected_type(context)?
|
||||
.with_position(expression.position);
|
||||
|
||||
fields.push((identifier.clone(), r#type));
|
||||
}
|
||||
|
||||
Type::Structure {
|
||||
name: name.clone(),
|
||||
fields,
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Ok(r#type)
|
||||
}
|
||||
|
||||
pub fn as_boolean(&self) -> Option<bool> {
|
||||
@ -176,6 +198,7 @@ impl Display for Value {
|
||||
ValueInner::Function(Function::BuiltIn(built_in_function)) => {
|
||||
write!(f, "{built_in_function}")
|
||||
}
|
||||
ValueInner::Structure { name, fields } => todo!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -204,6 +227,10 @@ pub enum ValueInner {
|
||||
Map(BTreeMap<Identifier, Value>),
|
||||
Range(Range<i64>),
|
||||
String(String),
|
||||
Structure {
|
||||
name: Identifier,
|
||||
fields: Vec<(Identifier, WithPosition<Expression>)>,
|
||||
},
|
||||
}
|
||||
|
||||
impl Eq for ValueInner {}
|
||||
@ -243,6 +270,25 @@ impl Ord for ValueInner {
|
||||
(String(_), _) => Ordering::Greater,
|
||||
(Function(left), Function(right)) => left.cmp(right),
|
||||
(Function(_), _) => Ordering::Greater,
|
||||
(
|
||||
Structure {
|
||||
name: left_name,
|
||||
fields: left_fields,
|
||||
},
|
||||
Structure {
|
||||
name: right_name,
|
||||
fields: right_fields,
|
||||
},
|
||||
) => {
|
||||
let name_cmp = left_name.cmp(right_name);
|
||||
|
||||
if name_cmp.is_eq() {
|
||||
left_fields.cmp(right_fields)
|
||||
} else {
|
||||
name_cmp
|
||||
}
|
||||
}
|
||||
(Structure { .. }, _) => Ordering::Greater,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -347,7 +393,7 @@ impl BuiltInFunction {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn call(&self, arguments: Vec<Value>, _context: &Context) -> Result<Action, RuntimeError> {
|
||||
pub fn call(&self, arguments: Vec<Value>, context: &Context) -> Result<Action, RuntimeError> {
|
||||
match self {
|
||||
BuiltInFunction::IntParse => {
|
||||
let string = arguments.get(0).unwrap();
|
||||
@ -359,10 +405,18 @@ impl BuiltInFunction {
|
||||
|
||||
// Ok(Action::Return(Value::integer(integer)))
|
||||
} else {
|
||||
let mut actual = Vec::with_capacity(arguments.len());
|
||||
|
||||
for value in arguments {
|
||||
let r#type = value.r#type(context)?;
|
||||
|
||||
actual.push(r#type);
|
||||
}
|
||||
|
||||
Err(RuntimeError::ValidationFailure(
|
||||
ValidationError::WrongArguments {
|
||||
expected: vec![Type::String],
|
||||
actual: arguments.iter().map(|value| value.r#type()).collect(),
|
||||
actual,
|
||||
},
|
||||
))
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user