Compare commits
7 Commits
76f7524fdb
...
4c6a93302d
Author | SHA1 | Date | |
---|---|---|---|
|
4c6a93302d | ||
|
a3f2539993 | ||
|
d9f53abeb2 | ||
|
f28f4eab78 | ||
|
f4d3282090 | ||
|
7289504ab7 | ||
|
cd1bb91555 |
5
TODO.md
5
TODO.md
@ -1,5 +1,10 @@
|
||||
# Immediate TODOs / General Code Cleanup
|
||||
|
||||
|
||||
## Evaluator
|
||||
|
||||
* Make the evaluator take ReducedIR items by reference
|
||||
|
||||
## Testing
|
||||
|
||||
* Make an automatic (macro-based?) system for numbering compiler errors, this should be every type of error
|
||||
|
@ -54,6 +54,14 @@ pub enum StatementKind {
|
||||
Declaration(Declaration),
|
||||
Import(ImportSpecifier),
|
||||
Module(ModuleSpecifier),
|
||||
Flow(FlowControl),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum FlowControl {
|
||||
Continue,
|
||||
Break,
|
||||
Return(Option<Expression>),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Default)]
|
||||
|
@ -47,6 +47,12 @@ pub fn walk_block<V: ASTVisitor>(v: &mut V, block: &Block) {
|
||||
if let Recursion::Continue = v.module(module_spec) {
|
||||
walk_block(v, &module_spec.contents);
|
||||
},
|
||||
Flow(ref flow_control) => match flow_control {
|
||||
FlowControl::Return(Some(ref retval)) => {
|
||||
walk_expression(v, retval);
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,9 @@
|
||||
#![allow(clippy::single_char_add_str)]
|
||||
use std::fmt::Write;
|
||||
|
||||
use super::{
|
||||
Block, Declaration, Expression, ExpressionKind, ImportSpecifier, InvocationArgument, ModuleSpecifier,
|
||||
Signature, Statement, StatementKind, AST,
|
||||
Block, Declaration, Expression, ExpressionKind, FlowControl, ImportSpecifier, InvocationArgument,
|
||||
ModuleSpecifier, Signature, Statement, StatementKind, AST,
|
||||
};
|
||||
|
||||
const LEVEL: usize = 2;
|
||||
@ -35,6 +37,7 @@ fn render_statement(stmt: &Statement, indent: usize, buf: &mut String) {
|
||||
Declaration(ref decl) => render_declaration(decl, indent, buf),
|
||||
Import(ref spec) => render_import(spec, indent, buf),
|
||||
Module(ref spec) => render_module(spec, indent, buf),
|
||||
Flow(ref flow_control) => render_flow_control(flow_control, indent, buf),
|
||||
}
|
||||
}
|
||||
|
||||
@ -191,6 +194,15 @@ fn render_module(_expr: &ModuleSpecifier, _indent: usize, buf: &mut String) {
|
||||
buf.push_str("(Module <some mod>)");
|
||||
}
|
||||
|
||||
fn render_flow_control(flow: &FlowControl, _indent: usize, buf: &mut String) {
|
||||
use FlowControl::*;
|
||||
match flow {
|
||||
Return(ref _expr) => write!(buf, "return <expr>").unwrap(),
|
||||
Break => write!(buf, "break").unwrap(),
|
||||
Continue => write!(buf, "continue").unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::render_ast;
|
||||
|
@ -33,6 +33,7 @@ pub enum Builtin {
|
||||
IOGetLine,
|
||||
Assignment,
|
||||
Concatenate,
|
||||
NotEqual,
|
||||
}
|
||||
|
||||
impl Builtin {
|
||||
@ -65,6 +66,7 @@ impl Builtin {
|
||||
Concatenate => ty!(StringT -> StringT -> StringT),
|
||||
Increment => ty!(Nat -> Int),
|
||||
Negate => ty!(Nat -> Int),
|
||||
NotEqual => ty!(Nat -> Nat -> Bool),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -116,6 +118,7 @@ impl FromStr for Builtin {
|
||||
"<" => LessThan,
|
||||
"<=" => LessThanOrEqual,
|
||||
"==" => Equality,
|
||||
"!=" => NotEqual,
|
||||
"=" => Assignment,
|
||||
"<=>" => Comparison,
|
||||
"print" => IOPrint,
|
||||
|
@ -15,7 +15,7 @@
|
||||
//! ```text
|
||||
//! program := (statement delimiter)* EOF
|
||||
//! delimiter := NEWLINE | ";"
|
||||
//! statement := expression | declaration | import | module
|
||||
//! statement := expression | declaration | import | module | flow
|
||||
//! block := "{" (statement delimiter)* "}"
|
||||
//! declaration := type_declaration | func_declaration | binding_declaration | impl_declaration
|
||||
//! ```
|
||||
@ -401,12 +401,28 @@ impl Parser {
|
||||
Keyword(Impl) => self.impl_declaration().map(StatementKind::Declaration),
|
||||
Keyword(Import) => self.import_declaration().map(StatementKind::Import),
|
||||
Keyword(Module) => self.module_declaration().map(StatementKind::Module),
|
||||
Keyword(Continue) | Keyword(Return) | Keyword(Break) =>
|
||||
self.flow_control().map(StatementKind::Flow),
|
||||
_ => self.expression().map(StatementKind::Expression),
|
||||
}?;
|
||||
let id = self.id_store.fresh();
|
||||
Ok(Statement { kind, id, location: tok.location })
|
||||
}
|
||||
|
||||
#[recursive_descent_method]
|
||||
fn flow_control(&mut self) -> ParseResult<FlowControl> {
|
||||
let tok = self.token_handler.next();
|
||||
Ok(match tok.get_kind() {
|
||||
Keyword(Continue) => FlowControl::Continue,
|
||||
Keyword(Break) => FlowControl::Break,
|
||||
Keyword(Return) => match self.token_handler.peek_kind() {
|
||||
Semicolon | Newline => FlowControl::Return(None),
|
||||
_ => FlowControl::Return(Some(self.expression()?)),
|
||||
},
|
||||
_ => unreachable!(),
|
||||
})
|
||||
}
|
||||
|
||||
#[recursive_descent_method]
|
||||
fn annotation(&mut self) -> ParseResult<Declaration> {
|
||||
expect!(self, AtSign);
|
||||
@ -1107,7 +1123,11 @@ impl Parser {
|
||||
let pat = self.pattern()?;
|
||||
(name, pat)
|
||||
}
|
||||
_ => (name.clone(), Pattern::Literal(PatternLiteral::StringPattern(name))),
|
||||
_ => {
|
||||
let qualified_identifier =
|
||||
QualifiedName { id: self.id_store.fresh(), components: vec![name.clone()] };
|
||||
(name, Pattern::VarOrName(qualified_identifier))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -1099,10 +1099,7 @@ fn pattern_matching() {
|
||||
body: bx(IfExpressionBody::SimplePatternMatch {
|
||||
pattern: Pattern::Record(
|
||||
qn!(Something),
|
||||
vec![
|
||||
(rc("a"), Pattern::Literal(PatternLiteral::StringPattern(rc("a")))),
|
||||
(rc("b"), Pattern::VarOrName(qn!(x)))
|
||||
]
|
||||
vec![(rc("a"), Pattern::VarOrName(qn!(a))), (rc("b"), Pattern::VarOrName(qn!(x)))]
|
||||
),
|
||||
then_case: vec![exst(NatLiteral(4))].into(),
|
||||
else_case: Some(vec![exst(NatLiteral(9))].into()),
|
||||
@ -1215,3 +1212,38 @@ if (45, "panda", false, 2.2) {
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn flow_control() {
|
||||
use ExpressionKind::*;
|
||||
|
||||
// This is an incorrect program, but shoudl parse correctly.
|
||||
let source = r#"
|
||||
fn test() {
|
||||
let a = 10;
|
||||
break;
|
||||
continue;
|
||||
return;
|
||||
return 10;
|
||||
}"#;
|
||||
|
||||
assert_ast!(
|
||||
source,
|
||||
vec![fn_decl(
|
||||
Signature { name: rc("test"), operator: false, type_anno: None, params: vec![] },
|
||||
vec![
|
||||
decl(Declaration::Binding {
|
||||
name: rc("a"),
|
||||
constant: true,
|
||||
type_anno: None,
|
||||
expr: expr(NatLiteral(10))
|
||||
}),
|
||||
stmt(StatementKind::Flow(FlowControl::Break)),
|
||||
stmt(StatementKind::Flow(FlowControl::Continue)),
|
||||
stmt(StatementKind::Flow(FlowControl::Return(None))),
|
||||
stmt(StatementKind::Flow(FlowControl::Return(Some(expr(NatLiteral(10)))))),
|
||||
]
|
||||
.into()
|
||||
)]
|
||||
);
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
use std::{collections::HashMap, str::FromStr};
|
||||
use std::{collections::HashMap, rc::Rc, str::FromStr};
|
||||
|
||||
use crate::{
|
||||
ast,
|
||||
@ -80,6 +80,9 @@ impl<'a, 'b> Reducer<'a, 'b> {
|
||||
ast::StatementKind::Module(_modspec) => {
|
||||
//TODO handle modules
|
||||
}
|
||||
ast::StatementKind::Flow(..) => {
|
||||
//TODO this should be an error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -100,7 +103,18 @@ impl<'a, 'b> Reducer<'a, 'b> {
|
||||
|
||||
_ => None,
|
||||
},
|
||||
_ => None,
|
||||
ast::StatementKind::Module(_) | ast::StatementKind::Import(_) => {
|
||||
//TODO need to handle function-internal modules, imports
|
||||
None
|
||||
}
|
||||
ast::StatementKind::Flow(ast::FlowControl::Return(expr)) =>
|
||||
if let Some(expr) = expr {
|
||||
Some(Statement::Return(self.expression(expr)))
|
||||
} else {
|
||||
Some(Statement::Return(Expression::unit()))
|
||||
},
|
||||
ast::StatementKind::Flow(ast::FlowControl::Break) => Some(Statement::Break),
|
||||
ast::StatementKind::Flow(ast::FlowControl::Continue) => Some(Statement::Continue),
|
||||
}
|
||||
}
|
||||
|
||||
@ -135,10 +149,9 @@ impl<'a, 'b> Reducer<'a, 'b> {
|
||||
}),
|
||||
NamedStruct { name, fields } => {
|
||||
self.symbol_table.debug();
|
||||
println!("Namedstruct name {} id: {}", name, name.id);
|
||||
let symbol = self.symbol_table.lookup_symbol(&name.id).unwrap();
|
||||
let (tag, type_id) = match symbol.spec() {
|
||||
SymbolSpec::RecordConstructor { tag, members: _, type_id } => (tag, type_id),
|
||||
SymbolSpec::RecordConstructor { tag, type_id } => (tag, type_id),
|
||||
e => return Expression::ReductionError(format!("Bad symbol for NamedStruct: {:?}", e)),
|
||||
};
|
||||
|
||||
@ -167,7 +180,15 @@ impl<'a, 'b> Reducer<'a, 'b> {
|
||||
Expression::Call { f: Box::new(constructor), args: ordered_args }
|
||||
}
|
||||
Index { .. } => Expression::ReductionError("Index expr not implemented".to_string()),
|
||||
WhileExpression { .. } => Expression::ReductionError("While expr not implemented".to_string()),
|
||||
WhileExpression { condition, body } => {
|
||||
let cond = Box::new(if let Some(condition) = condition {
|
||||
self.expression(condition)
|
||||
} else {
|
||||
Expression::Literal(Literal::Bool(true))
|
||||
});
|
||||
let statements = self.function_internal_block(body);
|
||||
Expression::Loop { cond, statements }
|
||||
}
|
||||
ForExpression { .. } => Expression::ReductionError("For expr not implemented".to_string()),
|
||||
ListLiteral { .. } => Expression::ReductionError("ListLiteral expr not implemented".to_string()),
|
||||
Access { name, expr } =>
|
||||
@ -372,11 +393,27 @@ impl ast::Pattern {
|
||||
spec => return Err(format!("Unexpected VarOrName symbol: {:?}", spec).into()),
|
||||
}
|
||||
}
|
||||
ast::Pattern::Record(name, _specified_members /*Vec<(Rc<String>, Pattern)>*/) => {
|
||||
ast::Pattern::Record(name, specified_members) => {
|
||||
let symbol = symbol_table.lookup_symbol(&name.id).unwrap();
|
||||
match symbol.spec() {
|
||||
SymbolSpec::RecordConstructor { tag: _, members: _, type_id: _ } => unimplemented!(),
|
||||
spec => return Err(format!("Unexpected Record pattern symbol: {:?}", spec).into()),
|
||||
if let SymbolSpec::RecordConstructor { tag, type_id: _ } = symbol.spec() {
|
||||
//TODO do this computation from the type_id
|
||||
/*
|
||||
if specified_members.iter().any(|(member, _)| !members.contains_key(member)) {
|
||||
return Err(format!("Unknown key in record pattern").into());
|
||||
}
|
||||
*/
|
||||
|
||||
let subpatterns: Result<Vec<(Rc<String>, Pattern)>, PatternError> = specified_members
|
||||
.iter()
|
||||
.map(|(name, pat)| {
|
||||
pat.reduce(symbol_table).map(|reduced_pat| (name.clone(), reduced_pat))
|
||||
})
|
||||
.into_iter()
|
||||
.collect();
|
||||
let subpatterns = subpatterns?;
|
||||
Pattern::Record { tag, subpatterns }
|
||||
} else {
|
||||
return Err(format!("Unexpected Record pattern symbol: {:?}", symbol.spec()).into());
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -42,6 +42,9 @@ impl ReducedIR {
|
||||
pub enum Statement {
|
||||
Expression(Expression),
|
||||
Binding { id: DefId, constant: bool, expr: Expression },
|
||||
Return(Expression),
|
||||
Continue,
|
||||
Break,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@ -55,6 +58,7 @@ pub enum Expression {
|
||||
Call { f: Box<Expression>, args: Vec<Expression> },
|
||||
Conditional { cond: Box<Expression>, then_clause: Vec<Statement>, else_clause: Vec<Statement> },
|
||||
CaseMatch { cond: Box<Expression>, alternatives: Vec<Alternative> },
|
||||
Loop { cond: Box<Expression>, statements: Vec<Statement> },
|
||||
ReductionError(String),
|
||||
}
|
||||
|
||||
@ -104,6 +108,7 @@ pub struct Alternative {
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Pattern {
|
||||
Tuple { subpatterns: Vec<Pattern>, tag: Option<u32> },
|
||||
Record { tag: u32, subpatterns: Vec<(Rc<String>, Pattern)> },
|
||||
Literal(Literal),
|
||||
Ignored,
|
||||
Binding(DefId),
|
||||
|
@ -282,7 +282,7 @@ pub enum SymbolSpec {
|
||||
Builtin(Builtin),
|
||||
Func,
|
||||
DataConstructor { tag: u32, type_id: TypeId },
|
||||
RecordConstructor { tag: u32, members: HashMap<Rc<String>, TypeId>, type_id: TypeId },
|
||||
RecordConstructor { tag: u32, type_id: TypeId },
|
||||
GlobalBinding, //Only for global variables, not for function-local ones or ones within a `let` scope context
|
||||
LocalVariable,
|
||||
FunctionParam(u8),
|
||||
@ -515,8 +515,7 @@ impl<'a> SymbolTableRunner<'a> {
|
||||
let spec = match &variant.members {
|
||||
type_inference::VariantMembers::Unit => SymbolSpec::DataConstructor { tag, type_id },
|
||||
type_inference::VariantMembers::Tuple(..) => SymbolSpec::DataConstructor { tag, type_id },
|
||||
type_inference::VariantMembers::Record(..) =>
|
||||
SymbolSpec::RecordConstructor { tag, members: HashMap::new(), type_id },
|
||||
type_inference::VariantMembers::Record(..) => SymbolSpec::RecordConstructor { tag, type_id },
|
||||
};
|
||||
self.table.add_symbol(id, fqsn, spec);
|
||||
}
|
||||
|
@ -86,11 +86,12 @@ pub enum Kw {
|
||||
Func,
|
||||
For,
|
||||
While,
|
||||
Const,
|
||||
Let,
|
||||
In,
|
||||
Mut,
|
||||
Return,
|
||||
Continue,
|
||||
Break,
|
||||
Alias,
|
||||
Type,
|
||||
SelfType,
|
||||
@ -115,11 +116,12 @@ impl TryFrom<&str> for Kw {
|
||||
"fn" => Kw::Func,
|
||||
"for" => Kw::For,
|
||||
"while" => Kw::While,
|
||||
"const" => Kw::Const,
|
||||
"let" => Kw::Let,
|
||||
"in" => Kw::In,
|
||||
"mut" => Kw::Mut,
|
||||
"return" => Kw::Return,
|
||||
"break" => Kw::Break,
|
||||
"continue" => Kw::Continue,
|
||||
"alias" => Kw::Alias,
|
||||
"type" => Kw::Type,
|
||||
"Self" => Kw::SelfType,
|
||||
|
@ -10,12 +10,30 @@ use crate::{
|
||||
util::ScopeStack,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
enum StatementOutput {
|
||||
Primitive(Primitive),
|
||||
Nothing,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
enum LoopControlFlow {
|
||||
Break,
|
||||
Continue,
|
||||
}
|
||||
|
||||
pub struct Evaluator<'a, 'b> {
|
||||
pub type_context: &'b TypeContext,
|
||||
pub state: &'b mut State<'a>,
|
||||
type_context: &'b TypeContext,
|
||||
state: &'b mut State<'a>,
|
||||
early_returning: bool,
|
||||
loop_control: Option<LoopControlFlow>,
|
||||
}
|
||||
|
||||
impl<'a, 'b> Evaluator<'a, 'b> {
|
||||
pub(crate) fn new(state: &'b mut State<'a>, type_context: &'b TypeContext) -> Self {
|
||||
Self { state, type_context, early_returning: false, loop_control: None }
|
||||
}
|
||||
|
||||
pub fn evaluate(&mut self, reduced: ReducedIR, repl: bool) -> Vec<Result<String, String>> {
|
||||
let mut acc = vec![];
|
||||
for (def_id, function) in reduced.functions.into_iter() {
|
||||
@ -25,7 +43,8 @@ impl<'a, 'b> Evaluator<'a, 'b> {
|
||||
|
||||
for statement in reduced.entrypoint.into_iter() {
|
||||
match self.statement(statement) {
|
||||
Ok(Some(output)) if repl => acc.push(Ok(output.to_repl(self.type_context))),
|
||||
Ok(StatementOutput::Primitive(output)) if repl =>
|
||||
acc.push(Ok(output.to_repl(self.type_context))),
|
||||
Ok(_) => (),
|
||||
Err(error) => {
|
||||
acc.push(Err(error.msg));
|
||||
@ -38,25 +57,47 @@ impl<'a, 'b> Evaluator<'a, 'b> {
|
||||
|
||||
fn block(&mut self, statements: Vec<Statement>) -> EvalResult<Primitive> {
|
||||
//TODO need to handle breaks, returns, etc.
|
||||
let mut ret = None;
|
||||
let mut retval = None;
|
||||
for stmt in statements.into_iter() {
|
||||
if let Some(MemoryValue::Primitive(prim)) = self.statement(stmt)? {
|
||||
ret = Some(prim);
|
||||
match self.statement(stmt)? {
|
||||
StatementOutput::Nothing => (),
|
||||
StatementOutput::Primitive(prim) => {
|
||||
retval = Some(prim);
|
||||
}
|
||||
};
|
||||
if self.early_returning {
|
||||
break;
|
||||
}
|
||||
if let Some(_) = self.loop_control {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Ok(if let Some(ret) = ret { ret } else { self.expression(Expression::unit())? })
|
||||
Ok(if let Some(ret) = retval { ret } else { self.expression(Expression::unit())? })
|
||||
}
|
||||
|
||||
fn statement(&mut self, stmt: Statement) -> EvalResult<Option<MemoryValue>> {
|
||||
fn statement(&mut self, stmt: Statement) -> EvalResult<StatementOutput> {
|
||||
match stmt {
|
||||
Statement::Binding { ref id, expr, constant: _ } => {
|
||||
let evaluated = self.expression(expr)?;
|
||||
self.state.environments.insert(id.into(), evaluated.into());
|
||||
Ok(None)
|
||||
Ok(StatementOutput::Nothing)
|
||||
}
|
||||
Statement::Expression(expr) => {
|
||||
let evaluated = self.expression(expr)?;
|
||||
Ok(Some(evaluated.into()))
|
||||
Ok(StatementOutput::Primitive(evaluated))
|
||||
}
|
||||
Statement::Return(expr) => {
|
||||
let evaluated = self.expression(expr)?;
|
||||
self.early_returning = true;
|
||||
Ok(StatementOutput::Primitive(evaluated))
|
||||
}
|
||||
Statement::Break => {
|
||||
self.loop_control = Some(LoopControlFlow::Break);
|
||||
Ok(StatementOutput::Nothing)
|
||||
}
|
||||
Statement::Continue => {
|
||||
self.loop_control = Some(LoopControlFlow::Continue);
|
||||
Ok(StatementOutput::Nothing)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -124,6 +165,7 @@ impl<'a, 'b> Evaluator<'a, 'b> {
|
||||
}
|
||||
Expression::CaseMatch { box cond, alternatives } =>
|
||||
self.case_match_expression(cond, alternatives)?,
|
||||
Expression::Loop { box cond, statements } => self.loop_expression(cond, statements)?,
|
||||
Expression::ReductionError(e) => return Err(e.into()),
|
||||
Expression::Access { name, box expr } => {
|
||||
let expr = self.expression(expr)?;
|
||||
@ -150,6 +192,37 @@ impl<'a, 'b> Evaluator<'a, 'b> {
|
||||
})
|
||||
}
|
||||
|
||||
fn loop_expression(&mut self, cond: Expression, statements: Vec<Statement>) -> EvalResult<Primitive> {
|
||||
let existing = self.loop_control;
|
||||
let output = self.loop_expression_inner(cond, statements);
|
||||
self.loop_control = existing;
|
||||
output
|
||||
}
|
||||
|
||||
fn loop_expression_inner(
|
||||
&mut self,
|
||||
cond: Expression,
|
||||
statements: Vec<Statement>,
|
||||
) -> EvalResult<Primitive> {
|
||||
loop {
|
||||
let cond = self.expression(cond.clone())?;
|
||||
match cond {
|
||||
Primitive::Literal(Literal::Bool(true)) => (),
|
||||
Primitive::Literal(Literal::Bool(false)) => break,
|
||||
e => return Err(format!("Loop condition evaluates to non-boolean: {:?}", e).into()),
|
||||
};
|
||||
//TODO eventually loops shoudl be able to return something
|
||||
let _output = self.block(statements.clone())?;
|
||||
match self.loop_control {
|
||||
None | Some(LoopControlFlow::Continue) => (),
|
||||
Some(LoopControlFlow::Break) => {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(Primitive::unit())
|
||||
}
|
||||
|
||||
fn case_match_expression(
|
||||
&mut self,
|
||||
cond: Expression,
|
||||
@ -188,6 +261,20 @@ impl<'a, 'b> Evaluator<'a, 'b> {
|
||||
_ => false,
|
||||
},
|
||||
},
|
||||
Pattern::Record { tag: pattern_tag, subpatterns } => match scrut {
|
||||
//TODO several types of possible error here
|
||||
Primitive::Object { tag, items, ordered_fields: Some(ordered_fields), .. }
|
||||
if tag == pattern_tag =>
|
||||
subpatterns.iter().all(|(field_name, subpat)| {
|
||||
let idx = ordered_fields
|
||||
.iter()
|
||||
.position(|field| field.as_str() == field_name.as_ref())
|
||||
.unwrap();
|
||||
let item = &items[idx];
|
||||
matches(item, subpat, scope)
|
||||
}),
|
||||
_ => false,
|
||||
},
|
||||
}
|
||||
}
|
||||
let cond = self.expression(cond)?;
|
||||
@ -196,9 +283,10 @@ impl<'a, 'b> Evaluator<'a, 'b> {
|
||||
let mut new_scope = self.state.environments.new_scope(None);
|
||||
if matches(&cond, &alt.pattern, &mut new_scope) {
|
||||
let mut new_state = State { environments: new_scope };
|
||||
let mut evaluator = Evaluator { state: &mut new_state, type_context: self.type_context };
|
||||
|
||||
return evaluator.block(alt.item);
|
||||
let mut evaluator = Evaluator::new(&mut new_state, self.type_context);
|
||||
let output = evaluator.block(alt.item);
|
||||
self.early_returning = evaluator.early_returning;
|
||||
return output;
|
||||
}
|
||||
}
|
||||
Err("No valid match in match expression".into())
|
||||
@ -319,6 +407,12 @@ impl<'a, 'b> Evaluator<'a, 'b> {
|
||||
(Equality, Lit(Bool(l)), Lit(Bool(r))) => Bool(l == r).into(),
|
||||
(Equality, Lit(StringLit(ref l)), Lit(StringLit(ref r))) => Bool(l == r).into(),
|
||||
|
||||
(NotEqual, Lit(Nat(l)), Lit(Nat(r))) => Bool(l != r).into(),
|
||||
(NotEqual, Lit(Int(l)), Lit(Int(r))) => Bool(l != r).into(),
|
||||
(NotEqual, Lit(Float(l)), Lit(Float(r))) => Bool(l != r).into(),
|
||||
(NotEqual, Lit(Bool(l)), Lit(Bool(r))) => Bool(l != r).into(),
|
||||
(NotEqual, Lit(StringLit(ref l)), Lit(StringLit(ref r))) => Bool(l != r).into(),
|
||||
|
||||
(LessThan, Lit(Nat(l)), Lit(Nat(r))) => Bool(l < r).into(),
|
||||
(LessThan, Lit(Int(l)), Lit(Int(r))) => Bool(l < r).into(),
|
||||
(LessThan, Lit(Float(l)), Lit(Float(r))) => Bool(l < r).into(),
|
||||
@ -360,7 +454,7 @@ impl<'a, 'b> Evaluator<'a, 'b> {
|
||||
}
|
||||
|
||||
let mut frame_state = State { environments: self.state.environments.new_scope(None) };
|
||||
let mut evaluator = Evaluator { state: &mut frame_state, type_context: self.type_context };
|
||||
let mut evaluator = Evaluator::new(&mut frame_state, self.type_context);
|
||||
|
||||
for (n, evaled) in evaluated_args.into_iter().enumerate() {
|
||||
let n = n as u8;
|
||||
|
@ -88,15 +88,6 @@ impl From<Primitive> for MemoryValue {
|
||||
}
|
||||
}
|
||||
|
||||
impl MemoryValue {
|
||||
fn to_repl(&self, type_context: &TypeContext) -> String {
|
||||
match self {
|
||||
MemoryValue::Primitive(ref prim) => prim.to_repl(type_context),
|
||||
MemoryValue::Function(..) => "<function>".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum RuntimeValue {
|
||||
Expression(Expression),
|
||||
@ -181,7 +172,7 @@ impl<'a> State<'a> {
|
||||
type_context: &TypeContext,
|
||||
repl: bool,
|
||||
) -> Vec<Result<String, String>> {
|
||||
let mut evaluator = evaluator::Evaluator { state: self, type_context };
|
||||
let mut evaluator = evaluator::Evaluator::new(self, type_context);
|
||||
evaluator.evaluate(reduced, repl)
|
||||
}
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ fn evaluate_input(input: &str) -> Result<String, String> {
|
||||
symbol_table.debug();
|
||||
|
||||
let mut state = State::new();
|
||||
let mut evaluator = Evaluator { state: &mut state, type_context: &type_context };
|
||||
let mut evaluator = Evaluator::new(&mut state, &type_context);
|
||||
let mut outputs = evaluator.evaluate(reduced_ir, true);
|
||||
outputs.pop().unwrap()
|
||||
}
|
||||
@ -172,6 +172,29 @@ if x {
|
||||
eval_assert(&source, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn record_patterns() {
|
||||
let source = r#"
|
||||
type Ara = Kueh { a: Int, b: String } | Morbuk
|
||||
|
||||
let alpha = Ara::Kueh { a: 10, b: "sanchez" }
|
||||
if alpha {
|
||||
is Ara::Kueh { a, b } then (b, a)
|
||||
is _ then ("nooo", 8888)
|
||||
}"#;
|
||||
eval_assert(source, r#"("sanchez", 10)"#);
|
||||
|
||||
let source = r#"
|
||||
type Ara = Kueh { a: Int, b: String } | Morbuk
|
||||
|
||||
let alpha = Ara::Kueh { a: 10, b: "sanchez" }
|
||||
if alpha {
|
||||
is Ara::Kueh { a, b: le_value } then (le_value, (a*2))
|
||||
is _ then ("nooo", 8888)
|
||||
}"#;
|
||||
eval_assert(source, r#"("sanchez", 20)"#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn if_is_patterns() {
|
||||
let source = r#"
|
||||
@ -378,3 +401,53 @@ let value = Klewos::Klewos { a: 50, b: "nah" }
|
||||
|
||||
eval_assert(source, r#"(50, "nah")"#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn early_return() {
|
||||
let source = r#"
|
||||
fn chnurmek(a: Int): Int {
|
||||
if a == 5 then {
|
||||
return 9999;
|
||||
}
|
||||
return (a + 2);
|
||||
}
|
||||
|
||||
(chnurmek(5), chnurmek(0))
|
||||
"#;
|
||||
eval_assert(source, r#"(9999, 2)"#);
|
||||
|
||||
let source = r#"
|
||||
fn marbuk(a: Int, b: Int): (Int, Int) {
|
||||
if a == 5 then {
|
||||
if b == 6 then {
|
||||
return (50, 50);
|
||||
}
|
||||
|
||||
return (a, b + 1)
|
||||
}
|
||||
(a * 100, b * 100)
|
||||
}
|
||||
|
||||
let x = marbuk(1, 1)
|
||||
let y = marbuk(5, 1)
|
||||
let z = marbuk(5, 6)
|
||||
|
||||
(x, y, z)
|
||||
"#;
|
||||
eval_assert(source, "((100, 100), (5, 2), (50, 50))");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn loops() {
|
||||
let source = r#"
|
||||
let mut a = 0
|
||||
let mut count = 0
|
||||
while a != 5 {
|
||||
a = a + 1
|
||||
count = count + 100
|
||||
}
|
||||
|
||||
count
|
||||
"#;
|
||||
eval_assert(source, "500");
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user