Fix function validation and parsing
This commit is contained in:
parent
e272d99bae
commit
2dd1628bca
@ -42,6 +42,8 @@ impl AbstractTree for Assignment {
|
||||
}
|
||||
|
||||
fn validate(&self, context: &Context) -> Result<(), ValidationError> {
|
||||
self.statement.validate(context)?;
|
||||
|
||||
let statement_type = self.statement.expected_type(context)?;
|
||||
|
||||
if let Some(expected) = &self.r#type {
|
||||
|
@ -1,7 +1,6 @@
|
||||
use crate::{
|
||||
context::Context,
|
||||
error::{RuntimeError, ValidationError},
|
||||
Value,
|
||||
};
|
||||
|
||||
use super::{AbstractTree, Action, Statement, Type};
|
||||
@ -19,9 +18,11 @@ impl Block {
|
||||
|
||||
impl AbstractTree for Block {
|
||||
fn expected_type(&self, _context: &Context) -> Result<Type, ValidationError> {
|
||||
let final_statement = self.statements.last().unwrap();
|
||||
|
||||
final_statement.expected_type(_context)
|
||||
if let Some(statement) = self.statements.last() {
|
||||
statement.expected_type(_context)
|
||||
} else {
|
||||
Ok(Type::None)
|
||||
}
|
||||
}
|
||||
|
||||
fn validate(&self, _context: &Context) -> Result<(), ValidationError> {
|
||||
@ -33,23 +34,26 @@ impl AbstractTree for Block {
|
||||
}
|
||||
|
||||
fn run(self, _context: &Context) -> Result<Action, RuntimeError> {
|
||||
let mut previous = Value::none();
|
||||
let mut previous = Action::None;
|
||||
|
||||
for statement in self.statements {
|
||||
let action = statement.run(_context)?;
|
||||
previous = match action {
|
||||
Action::Return(value) => value,
|
||||
Action::Return(value) => Action::Return(value),
|
||||
r#break => return Ok(r#break),
|
||||
};
|
||||
}
|
||||
|
||||
Ok(Action::Return(previous))
|
||||
Ok(previous)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::abstract_tree::{Expression, ValueNode};
|
||||
use crate::{
|
||||
abstract_tree::{Expression, ValueNode},
|
||||
Value,
|
||||
};
|
||||
|
||||
use super::*;
|
||||
|
||||
|
@ -6,7 +6,7 @@ use crate::{
|
||||
Value,
|
||||
};
|
||||
|
||||
use super::{AbstractTree, Action, Expression, Identifier, Statement, Type};
|
||||
use super::{AbstractTree, Action, Block, Expression, Identifier, Type};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum ValueNode {
|
||||
@ -21,7 +21,7 @@ pub enum ValueNode {
|
||||
Function {
|
||||
parameters: Vec<(Identifier, 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(())
|
||||
}
|
||||
|
||||
@ -115,7 +132,7 @@ impl AbstractTree for ValueNode {
|
||||
parameters,
|
||||
return_type,
|
||||
body,
|
||||
} => Value::function(parameters, return_type, *body),
|
||||
} => Value::function(parameters, return_type, body),
|
||||
};
|
||||
|
||||
Ok(Action::Return(value))
|
||||
|
@ -210,6 +210,7 @@ pub fn lexer<'src>() -> impl Parser<
|
||||
.map(Token::Control);
|
||||
|
||||
let keyword = choice((
|
||||
just("any").padded(),
|
||||
just("bool").padded(),
|
||||
just("break").padded(),
|
||||
just("else").padded(),
|
||||
@ -218,6 +219,7 @@ pub fn lexer<'src>() -> impl Parser<
|
||||
just("if").padded(),
|
||||
just("list").padded(),
|
||||
just("map").padded(),
|
||||
just("none").padded(),
|
||||
just("range").padded(),
|
||||
just("str").padded(),
|
||||
just("loop").padded(),
|
||||
|
136
src/parser.rs
136
src/parser.rs
@ -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((
|
||||
just(Token::Keyword("any")).to(Type::Any),
|
||||
just(Token::Keyword("bool")).to(Type::Boolean),
|
||||
just(Token::Keyword("float")).to(Type::Float),
|
||||
just(Token::Keyword("int")).to(Type::Integer),
|
||||
just(Token::Keyword("none")).to(Type::None),
|
||||
just(Token::Keyword("range")).to(Type::Range),
|
||||
just(Token::Keyword("str")).to(Type::String),
|
||||
just(Token::Keyword("list")).to(Type::List),
|
||||
@ -80,16 +91,18 @@ pub fn parser<'src>() -> DustParser<'src> {
|
||||
.map(|identifier| Type::Custom(identifier)),
|
||||
)));
|
||||
|
||||
let expression = recursive(|expression| {
|
||||
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 statement = recursive(|statement| {
|
||||
let block = statement
|
||||
.clone()
|
||||
.repeated()
|
||||
.collect()
|
||||
.delimited_by(
|
||||
just(Token::Control(Control::CurlyOpen)),
|
||||
just(Token::Control(Control::CurlyClose)),
|
||||
)
|
||||
.map(|statements| Block::new(statements));
|
||||
|
||||
let expression = recursive(|expression| {
|
||||
let identifier_expression = identifier
|
||||
.clone()
|
||||
.map(|identifier| Expression::Identifier(identifier))
|
||||
@ -143,7 +156,46 @@ pub fn parser<'src>() -> DustParser<'src> {
|
||||
.map(|(name, variant)| Expression::Value(ValueNode::Enum(name, variant)))
|
||||
.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.clone())
|
||||
.then(block.clone())
|
||||
.map(|((parameters, return_type), body)| {
|
||||
Expression::Value(ValueNode::Function {
|
||||
parameters,
|
||||
return_type,
|
||||
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(),
|
||||
@ -176,7 +228,9 @@ pub fn parser<'src>() -> DustParser<'src> {
|
||||
infix(
|
||||
left(1),
|
||||
just(Token::Operator(GreaterOrEqual)),
|
||||
|left, right| Expression::Logic(Box::new(Logic::GreaterOrEqual(left, right))),
|
||||
|left, right| {
|
||||
Expression::Logic(Box::new(Logic::GreaterOrEqual(left, right)))
|
||||
},
|
||||
),
|
||||
infix(
|
||||
left(1),
|
||||
@ -213,6 +267,7 @@ pub fn parser<'src>() -> DustParser<'src> {
|
||||
.boxed();
|
||||
|
||||
choice((
|
||||
function,
|
||||
range,
|
||||
r#enum,
|
||||
logic_math_and_index,
|
||||
@ -221,9 +276,9 @@ pub fn parser<'src>() -> DustParser<'src> {
|
||||
map,
|
||||
basic_value,
|
||||
))
|
||||
.boxed()
|
||||
});
|
||||
|
||||
let statement = recursive(|statement| {
|
||||
let expression_statement = expression
|
||||
.clone()
|
||||
.map(|expression| Statement::Expression(expression))
|
||||
@ -247,16 +302,7 @@ pub fn parser<'src>() -> DustParser<'src> {
|
||||
})
|
||||
.boxed();
|
||||
|
||||
let block = statement
|
||||
.clone()
|
||||
.repeated()
|
||||
.collect()
|
||||
.delimited_by(
|
||||
just(Token::Control(Control::CurlyOpen)),
|
||||
just(Token::Control(Control::CurlyClose)),
|
||||
)
|
||||
.map(|statements| Statement::Block(Block::new(statements)))
|
||||
.boxed();
|
||||
let block_statement = block.clone().map(|block| Statement::Block(block));
|
||||
|
||||
let r#loop = statement
|
||||
.clone()
|
||||
@ -283,54 +329,16 @@ pub fn parser<'src>() -> DustParser<'src> {
|
||||
})
|
||||
.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((
|
||||
function_call,
|
||||
assignment,
|
||||
expression_statement,
|
||||
r#break,
|
||||
block,
|
||||
block_statement,
|
||||
r#loop,
|
||||
if_else,
|
||||
function,
|
||||
))
|
||||
.then_ignore(just(Token::Control(Control::Semicolon)).or_not())
|
||||
.boxed()
|
||||
});
|
||||
|
||||
statement
|
||||
@ -368,13 +376,13 @@ mod tests {
|
||||
#[test]
|
||||
fn function() {
|
||||
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 {
|
||||
parameters: vec![(Identifier::new("x"), Type::Integer)],
|
||||
return_type: Type::Integer,
|
||||
body: Box::new(Statement::Expression(Expression::Identifier(
|
||||
body: Block::new(vec![Statement::Expression(Expression::Identifier(
|
||||
Identifier::new("x")
|
||||
)))
|
||||
))])
|
||||
}))
|
||||
)
|
||||
}
|
||||
|
10
src/value.rs
10
src/value.rs
@ -13,7 +13,7 @@ use stanza::{
|
||||
};
|
||||
|
||||
use crate::{
|
||||
abstract_tree::{AbstractTree, Action, Identifier, Statement, Type},
|
||||
abstract_tree::{AbstractTree, Action, Block, Identifier, Type},
|
||||
context::Context,
|
||||
error::{RuntimeError, ValidationError},
|
||||
};
|
||||
@ -73,11 +73,7 @@ impl Value {
|
||||
Value(Arc::new(ValueInner::Enum(name, variant)))
|
||||
}
|
||||
|
||||
pub fn function(
|
||||
parameters: Vec<(Identifier, Type)>,
|
||||
return_type: Type,
|
||||
body: Statement,
|
||||
) -> Self {
|
||||
pub fn function(parameters: Vec<(Identifier, Type)>, return_type: Type, body: Block) -> Self {
|
||||
Value(Arc::new(ValueInner::Function(Function::Parsed(
|
||||
ParsedFunction {
|
||||
parameters,
|
||||
@ -369,7 +365,7 @@ impl Function {
|
||||
pub struct ParsedFunction {
|
||||
parameters: Vec<(Identifier, Type)>,
|
||||
return_type: Type,
|
||||
body: Statement,
|
||||
body: Block,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
|
||||
|
@ -38,7 +38,7 @@ fn callback() {
|
||||
foobar = (cb : () -> str) : str {
|
||||
cb()
|
||||
}
|
||||
foobar(() : str { 'Hiya' })
|
||||
foobar(() : str 'Hiya')
|
||||
",
|
||||
),
|
||||
Ok(Some(Value::string("Hiya".to_string())))
|
||||
@ -62,7 +62,7 @@ fn function_context_does_not_capture_values() {
|
||||
),
|
||||
Err(vec![Error::Validation {
|
||||
error: ValidationError::VariableNotFound(Identifier::new("x")),
|
||||
span: (0..0).into()
|
||||
span: (32..66).into()
|
||||
}])
|
||||
);
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
use dust_lang::{
|
||||
abstract_tree::{Expression, Identifier, Statement, Type},
|
||||
abstract_tree::{Block, Expression, Identifier, Statement, Type},
|
||||
error::{Error, TypeCheckError, ValidationError},
|
||||
*,
|
||||
};
|
||||
@ -41,7 +41,9 @@ fn function_variable() {
|
||||
Ok(Some(Value::function(
|
||||
vec![(Identifier::new("x"), Type::Integer)],
|
||||
Type::Integer,
|
||||
Statement::Expression(Expression::Identifier(Identifier::new("x")))
|
||||
Block::new(vec![Statement::Expression(Expression::Identifier(
|
||||
Identifier::new("x")
|
||||
))])
|
||||
)))
|
||||
);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user