1
0

Fix function validation and parsing

This commit is contained in:
Jeff 2024-03-09 12:58:29 -05:00
parent e272d99bae
commit 2dd1628bca
8 changed files with 238 additions and 207 deletions

View File

@ -42,6 +42,8 @@ impl AbstractTree for Assignment {
} }
fn validate(&self, context: &Context) -> Result<(), ValidationError> { fn validate(&self, context: &Context) -> Result<(), ValidationError> {
self.statement.validate(context)?;
let statement_type = self.statement.expected_type(context)?; let statement_type = self.statement.expected_type(context)?;
if let Some(expected) = &self.r#type { if let Some(expected) = &self.r#type {

View File

@ -1,7 +1,6 @@
use crate::{ use crate::{
context::Context, context::Context,
error::{RuntimeError, ValidationError}, error::{RuntimeError, ValidationError},
Value,
}; };
use super::{AbstractTree, Action, Statement, Type}; use super::{AbstractTree, Action, Statement, Type};
@ -19,9 +18,11 @@ impl Block {
impl AbstractTree for Block { impl AbstractTree for Block {
fn expected_type(&self, _context: &Context) -> Result<Type, ValidationError> { fn expected_type(&self, _context: &Context) -> Result<Type, ValidationError> {
let final_statement = self.statements.last().unwrap(); if let Some(statement) = self.statements.last() {
statement.expected_type(_context)
final_statement.expected_type(_context) } else {
Ok(Type::None)
}
} }
fn validate(&self, _context: &Context) -> Result<(), ValidationError> { fn validate(&self, _context: &Context) -> Result<(), ValidationError> {
@ -33,23 +34,26 @@ impl AbstractTree for Block {
} }
fn run(self, _context: &Context) -> Result<Action, RuntimeError> { fn run(self, _context: &Context) -> Result<Action, RuntimeError> {
let mut previous = Value::none(); let mut previous = Action::None;
for statement in self.statements { for statement in self.statements {
let action = statement.run(_context)?; let action = statement.run(_context)?;
previous = match action { previous = match action {
Action::Return(value) => value, Action::Return(value) => Action::Return(value),
r#break => return Ok(r#break), r#break => return Ok(r#break),
}; };
} }
Ok(Action::Return(previous)) Ok(previous)
} }
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::abstract_tree::{Expression, ValueNode}; use crate::{
abstract_tree::{Expression, ValueNode},
Value,
};
use super::*; use super::*;

View File

@ -6,7 +6,7 @@ use crate::{
Value, Value,
}; };
use super::{AbstractTree, Action, Expression, Identifier, Statement, Type}; use super::{AbstractTree, Action, Block, Expression, Identifier, Type};
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub enum ValueNode { pub enum ValueNode {
@ -21,7 +21,7 @@ pub enum ValueNode {
Function { Function {
parameters: Vec<(Identifier, Type)>, parameters: Vec<(Identifier, Type)>,
return_type: Type, return_type: Type,
body: Box<Statement>, body: Block,
}, },
} }
@ -72,6 +72,23 @@ impl AbstractTree for ValueNode {
} }
} }
if let ValueNode::Function {
parameters,
return_type,
body,
} = self
{
let function_context = Context::new();
for (identifier, r#type) in parameters {
function_context.set_type(identifier.clone(), r#type.clone())?;
}
let actual_return_type = body.expected_type(&function_context)?;
return_type.check(&actual_return_type)?;
}
Ok(()) Ok(())
} }
@ -115,7 +132,7 @@ impl AbstractTree for ValueNode {
parameters, parameters,
return_type, return_type,
body, body,
} => Value::function(parameters, return_type, *body), } => Value::function(parameters, return_type, body),
}; };
Ok(Action::Return(value)) Ok(Action::Return(value))

View File

@ -210,6 +210,7 @@ pub fn lexer<'src>() -> impl Parser<
.map(Token::Control); .map(Token::Control);
let keyword = choice(( let keyword = choice((
just("any").padded(),
just("bool").padded(), just("bool").padded(),
just("break").padded(), just("break").padded(),
just("else").padded(), just("else").padded(),
@ -218,6 +219,7 @@ pub fn lexer<'src>() -> impl Parser<
just("if").padded(), just("if").padded(),
just("list").padded(), just("list").padded(),
just("map").padded(), just("map").padded(),
just("none").padded(),
just("range").padded(), just("range").padded(),
just("str").padded(), just("str").padded(),
just("loop").padded(), just("loop").padded(),

View File

@ -47,10 +47,21 @@ pub fn parser<'src>() -> DustParser<'src> {
} }
}; };
let basic_value = select! {
Token::Boolean(boolean) => ValueNode::Boolean(boolean),
Token::Integer(integer) => ValueNode::Integer(integer),
Token::Float(float) => ValueNode::Float(float),
Token::String(string) => ValueNode::String(string.to_string()),
}
.map(|value| Expression::Value(value))
.boxed();
let basic_type = choice(( let basic_type = choice((
just(Token::Keyword("any")).to(Type::Any),
just(Token::Keyword("bool")).to(Type::Boolean), just(Token::Keyword("bool")).to(Type::Boolean),
just(Token::Keyword("float")).to(Type::Float), just(Token::Keyword("float")).to(Type::Float),
just(Token::Keyword("int")).to(Type::Integer), just(Token::Keyword("int")).to(Type::Integer),
just(Token::Keyword("none")).to(Type::None),
just(Token::Keyword("range")).to(Type::Range), just(Token::Keyword("range")).to(Type::Range),
just(Token::Keyword("str")).to(Type::String), just(Token::Keyword("str")).to(Type::String),
just(Token::Keyword("list")).to(Type::List), just(Token::Keyword("list")).to(Type::List),
@ -80,150 +91,194 @@ pub fn parser<'src>() -> DustParser<'src> {
.map(|identifier| Type::Custom(identifier)), .map(|identifier| Type::Custom(identifier)),
))); )));
let expression = recursive(|expression| { let statement = recursive(|statement| {
let basic_value = select! { let block = statement
Token::Boolean(boolean) => ValueNode::Boolean(boolean),
Token::Integer(integer) => ValueNode::Integer(integer),
Token::Float(float) => ValueNode::Float(float),
Token::String(string) => ValueNode::String(string.to_string()),
}
.map(|value| Expression::Value(value))
.boxed();
let identifier_expression = identifier
.clone() .clone()
.map(|identifier| Expression::Identifier(identifier)) .repeated()
.boxed();
let range = {
let raw_integer = select! {
Token::Integer(integer) => integer
};
raw_integer
.clone()
.then_ignore(just(Token::Control(Control::DoubleDot)))
.then(raw_integer)
.map(|(start, end)| Expression::Value(ValueNode::Range(start..end)))
};
let list = expression
.clone()
.separated_by(just(Token::Control(Control::Comma)))
.allow_trailing()
.collect()
.delimited_by(
just(Token::Control(Control::SquareOpen)),
just(Token::Control(Control::SquareClose)),
)
.map(|list| Expression::Value(ValueNode::List(list)))
.boxed();
let map_assignment = identifier
.clone()
.then(type_specification.clone().or_not())
.then_ignore(just(Token::Operator(Operator::Assign)))
.then(expression.clone())
.map(|((identifier, r#type), expression)| (identifier, r#type, expression));
let map = map_assignment
.separated_by(just(Token::Control(Control::Comma)).or_not())
.allow_trailing()
.collect() .collect()
.delimited_by( .delimited_by(
just(Token::Control(Control::CurlyOpen)), just(Token::Control(Control::CurlyOpen)),
just(Token::Control(Control::CurlyClose)), just(Token::Control(Control::CurlyClose)),
) )
.map(|map_assigment_list| Expression::Value(ValueNode::Map(map_assigment_list))); .map(|statements| Block::new(statements));
let r#enum = identifier let expression = recursive(|expression| {
.clone() let identifier_expression = identifier
.then_ignore(just(Token::Control(Control::DoubleColon))) .clone()
.then(identifier.clone()) .map(|identifier| Expression::Identifier(identifier))
.map(|(name, variant)| Expression::Value(ValueNode::Enum(name, variant))) .boxed();
.boxed();
let atom = choice(( let range = {
identifier_expression.clone(), let raw_integer = select! {
basic_value.clone(), Token::Integer(integer) => integer
list.clone(), };
r#enum.clone(),
expression.clone().delimited_by(
just(Token::Control(Control::ParenOpen)),
just(Token::Control(Control::ParenClose)),
),
));
use Operator::*; raw_integer
.clone()
.then_ignore(just(Token::Control(Control::DoubleDot)))
.then(raw_integer)
.map(|(start, end)| Expression::Value(ValueNode::Range(start..end)))
};
let logic_math_and_index = atom let list = expression
.pratt(( .clone()
prefix(2, just(Token::Operator(Not)), |expression| { .separated_by(just(Token::Control(Control::Comma)))
Expression::Logic(Box::new(Logic::Not(expression))) .allow_trailing()
}), .collect()
infix(left(1), just(Token::Operator(Equal)), |left, right| { .delimited_by(
Expression::Logic(Box::new(Logic::Equal(left, right))) just(Token::Control(Control::SquareOpen)),
}), just(Token::Control(Control::SquareClose)),
infix(left(1), just(Token::Operator(NotEqual)), |left, right| { )
Expression::Logic(Box::new(Logic::NotEqual(left, right))) .map(|list| Expression::Value(ValueNode::List(list)))
}), .boxed();
infix(left(1), just(Token::Operator(Greater)), |left, right| {
Expression::Logic(Box::new(Logic::Greater(left, right))) let map_assignment = identifier
}), .clone()
infix(left(1), just(Token::Operator(Less)), |left, right| { .then(type_specification.clone().or_not())
Expression::Logic(Box::new(Logic::Less(left, right))) .then_ignore(just(Token::Operator(Operator::Assign)))
}), .then(expression.clone())
infix( .map(|((identifier, r#type), expression)| (identifier, r#type, expression));
left(1),
just(Token::Operator(GreaterOrEqual)), let map = map_assignment
|left, right| Expression::Logic(Box::new(Logic::GreaterOrEqual(left, right))), .separated_by(just(Token::Control(Control::Comma)).or_not())
), .allow_trailing()
infix( .collect()
left(1), .delimited_by(
just(Token::Operator(LessOrEqual)), just(Token::Control(Control::CurlyOpen)),
|left, right| Expression::Logic(Box::new(Logic::LessOrEqual(left, right))), just(Token::Control(Control::CurlyClose)),
), )
infix(left(1), just(Token::Operator(And)), |left, right| { .map(|map_assigment_list| Expression::Value(ValueNode::Map(map_assigment_list)));
Expression::Logic(Box::new(Logic::And(left, right)))
}), let r#enum = identifier
infix(left(1), just(Token::Operator(Or)), |left, right| { .clone()
Expression::Logic(Box::new(Logic::Or(left, right))) .then_ignore(just(Token::Control(Control::DoubleColon)))
}), .then(identifier.clone())
infix(left(1), just(Token::Operator(Add)), |left, right| { .map(|(name, variant)| Expression::Value(ValueNode::Enum(name, variant)))
Expression::Math(Box::new(Math::Add(left, right))) .boxed();
}),
infix(left(1), just(Token::Operator(Subtract)), |left, right| { let function = identifier
Expression::Math(Box::new(Math::Subtract(left, right))) .clone()
}), .then(type_specification.clone())
infix(left(2), just(Token::Operator(Multiply)), |left, right| { .separated_by(just(Token::Control(Control::Comma)))
Expression::Math(Box::new(Math::Multiply(left, right))) .collect::<Vec<(Identifier, Type)>>()
}), .delimited_by(
infix(left(2), just(Token::Operator(Divide)), |left, right| { just(Token::Control(Control::ParenOpen)),
Expression::Math(Box::new(Math::Divide(left, right))) just(Token::Control(Control::ParenClose)),
}), )
infix(left(1), just(Token::Operator(Modulo)), |left, right| { .then(type_specification.clone())
Expression::Math(Box::new(Math::Modulo(left, right))) .then(block.clone())
}), .map(|((parameters, return_type), body)| {
infix( Expression::Value(ValueNode::Function {
left(3), parameters,
just(Token::Control(Control::Dot)), return_type,
|left, right| Expression::Index(Box::new(Index::new(left, right))), body,
})
})
.boxed();
let function_expression = choice((identifier_expression.clone(), function.clone()));
let function_call = function_expression
.then(
expression
.clone()
.separated_by(just(Token::Control(Control::Comma)))
.collect()
.delimited_by(
just(Token::Control(Control::ParenOpen)),
just(Token::Control(Control::ParenClose)),
),
)
.map(|(function, arguments)| {
Expression::FunctionCall(FunctionCall::new(function, arguments))
})
.boxed();
let atom = choice((
function_call,
identifier_expression.clone(),
basic_value.clone(),
list.clone(),
r#enum.clone(),
expression.clone().delimited_by(
just(Token::Control(Control::ParenOpen)),
just(Token::Control(Control::ParenClose)),
), ),
));
use Operator::*;
let logic_math_and_index = atom
.pratt((
prefix(2, just(Token::Operator(Not)), |expression| {
Expression::Logic(Box::new(Logic::Not(expression)))
}),
infix(left(1), just(Token::Operator(Equal)), |left, right| {
Expression::Logic(Box::new(Logic::Equal(left, right)))
}),
infix(left(1), just(Token::Operator(NotEqual)), |left, right| {
Expression::Logic(Box::new(Logic::NotEqual(left, right)))
}),
infix(left(1), just(Token::Operator(Greater)), |left, right| {
Expression::Logic(Box::new(Logic::Greater(left, right)))
}),
infix(left(1), just(Token::Operator(Less)), |left, right| {
Expression::Logic(Box::new(Logic::Less(left, right)))
}),
infix(
left(1),
just(Token::Operator(GreaterOrEqual)),
|left, right| {
Expression::Logic(Box::new(Logic::GreaterOrEqual(left, right)))
},
),
infix(
left(1),
just(Token::Operator(LessOrEqual)),
|left, right| Expression::Logic(Box::new(Logic::LessOrEqual(left, right))),
),
infix(left(1), just(Token::Operator(And)), |left, right| {
Expression::Logic(Box::new(Logic::And(left, right)))
}),
infix(left(1), just(Token::Operator(Or)), |left, right| {
Expression::Logic(Box::new(Logic::Or(left, right)))
}),
infix(left(1), just(Token::Operator(Add)), |left, right| {
Expression::Math(Box::new(Math::Add(left, right)))
}),
infix(left(1), just(Token::Operator(Subtract)), |left, right| {
Expression::Math(Box::new(Math::Subtract(left, right)))
}),
infix(left(2), just(Token::Operator(Multiply)), |left, right| {
Expression::Math(Box::new(Math::Multiply(left, right)))
}),
infix(left(2), just(Token::Operator(Divide)), |left, right| {
Expression::Math(Box::new(Math::Divide(left, right)))
}),
infix(left(1), just(Token::Operator(Modulo)), |left, right| {
Expression::Math(Box::new(Math::Modulo(left, right)))
}),
infix(
left(3),
just(Token::Control(Control::Dot)),
|left, right| Expression::Index(Box::new(Index::new(left, right))),
),
))
.boxed();
choice((
function,
range,
r#enum,
logic_math_and_index,
identifier_expression,
list,
map,
basic_value,
)) ))
.boxed(); .boxed()
});
choice((
range,
r#enum,
logic_math_and_index,
identifier_expression,
list,
map,
basic_value,
))
});
let statement = recursive(|statement| {
let expression_statement = expression let expression_statement = expression
.clone() .clone()
.map(|expression| Statement::Expression(expression)) .map(|expression| Statement::Expression(expression))
@ -247,16 +302,7 @@ pub fn parser<'src>() -> DustParser<'src> {
}) })
.boxed(); .boxed();
let block = statement let block_statement = block.clone().map(|block| Statement::Block(block));
.clone()
.repeated()
.collect()
.delimited_by(
just(Token::Control(Control::CurlyOpen)),
just(Token::Control(Control::CurlyClose)),
)
.map(|statements| Statement::Block(Block::new(statements)))
.boxed();
let r#loop = statement let r#loop = statement
.clone() .clone()
@ -283,54 +329,16 @@ pub fn parser<'src>() -> DustParser<'src> {
}) })
.boxed(); .boxed();
let function = identifier
.clone()
.then(type_specification.clone())
.separated_by(just(Token::Control(Control::Comma)))
.collect::<Vec<(Identifier, Type)>>()
.delimited_by(
just(Token::Control(Control::ParenOpen)),
just(Token::Control(Control::ParenClose)),
)
.then(type_specification)
.then(statement.clone())
.map(|((parameters, return_type), body)| {
Statement::Expression(Expression::Value(ValueNode::Function {
parameters,
return_type,
body: Box::new(body),
}))
});
let function_call = expression
.clone()
.then(
expression
.clone()
.separated_by(just(Token::Control(Control::Comma)))
.collect()
.delimited_by(
just(Token::Control(Control::ParenOpen)),
just(Token::Control(Control::ParenClose)),
),
)
.map(|(function, arguments)| {
Statement::Expression(Expression::FunctionCall(FunctionCall::new(
function, arguments,
)))
});
choice(( choice((
function_call,
assignment, assignment,
expression_statement, expression_statement,
r#break, r#break,
block, block_statement,
r#loop, r#loop,
if_else, if_else,
function,
)) ))
.then_ignore(just(Token::Control(Control::Semicolon)).or_not()) .then_ignore(just(Token::Control(Control::Semicolon)).or_not())
.boxed()
}); });
statement statement
@ -368,13 +376,13 @@ mod tests {
#[test] #[test]
fn function() { fn function() {
assert_eq!( assert_eq!(
parse(&lex("(x: int): int x ").unwrap()).unwrap()[0].0, parse(&lex("(x: int): int { x }").unwrap()).unwrap()[0].0,
Statement::Expression(Expression::Value(ValueNode::Function { Statement::Expression(Expression::Value(ValueNode::Function {
parameters: vec![(Identifier::new("x"), Type::Integer)], parameters: vec![(Identifier::new("x"), Type::Integer)],
return_type: Type::Integer, return_type: Type::Integer,
body: Box::new(Statement::Expression(Expression::Identifier( body: Block::new(vec![Statement::Expression(Expression::Identifier(
Identifier::new("x") Identifier::new("x")
))) ))])
})) }))
) )
} }

View File

@ -13,7 +13,7 @@ use stanza::{
}; };
use crate::{ use crate::{
abstract_tree::{AbstractTree, Action, Identifier, Statement, Type}, abstract_tree::{AbstractTree, Action, Block, Identifier, Type},
context::Context, context::Context,
error::{RuntimeError, ValidationError}, error::{RuntimeError, ValidationError},
}; };
@ -73,11 +73,7 @@ impl Value {
Value(Arc::new(ValueInner::Enum(name, variant))) Value(Arc::new(ValueInner::Enum(name, variant)))
} }
pub fn function( pub fn function(parameters: Vec<(Identifier, Type)>, return_type: Type, body: Block) -> Self {
parameters: Vec<(Identifier, Type)>,
return_type: Type,
body: Statement,
) -> Self {
Value(Arc::new(ValueInner::Function(Function::Parsed( Value(Arc::new(ValueInner::Function(Function::Parsed(
ParsedFunction { ParsedFunction {
parameters, parameters,
@ -369,7 +365,7 @@ impl Function {
pub struct ParsedFunction { pub struct ParsedFunction {
parameters: Vec<(Identifier, Type)>, parameters: Vec<(Identifier, Type)>,
return_type: Type, return_type: Type,
body: Statement, body: Block,
} }
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]

View File

@ -38,7 +38,7 @@ fn callback() {
foobar = (cb : () -> str) : str { foobar = (cb : () -> str) : str {
cb() cb()
} }
foobar(() : str { 'Hiya' }) foobar(() : str 'Hiya')
", ",
), ),
Ok(Some(Value::string("Hiya".to_string()))) Ok(Some(Value::string("Hiya".to_string())))
@ -62,7 +62,7 @@ fn function_context_does_not_capture_values() {
), ),
Err(vec![Error::Validation { Err(vec![Error::Validation {
error: ValidationError::VariableNotFound(Identifier::new("x")), error: ValidationError::VariableNotFound(Identifier::new("x")),
span: (0..0).into() span: (32..66).into()
}]) }])
); );

View File

@ -1,5 +1,5 @@
use dust_lang::{ use dust_lang::{
abstract_tree::{Expression, Identifier, Statement, Type}, abstract_tree::{Block, Expression, Identifier, Statement, Type},
error::{Error, TypeCheckError, ValidationError}, error::{Error, TypeCheckError, ValidationError},
*, *,
}; };
@ -41,7 +41,9 @@ fn function_variable() {
Ok(Some(Value::function( Ok(Some(Value::function(
vec![(Identifier::new("x"), Type::Integer)], vec![(Identifier::new("x"), Type::Integer)],
Type::Integer, Type::Integer,
Statement::Expression(Expression::Identifier(Identifier::new("x"))) Block::new(vec![Statement::Expression(Expression::Identifier(
Identifier::new("x")
))])
))) )))
); );
} }