From b0cc2fc26b533fdfa06f67b78ca092d26f15b0c1 Mon Sep 17 00:00:00 2001 From: Adam Wick Date: Fri, 8 Mar 2024 18:39:13 -0700 Subject: [PATCH] Broken structure now gets through syntax evaluator. --- examples/basic/broken0001.ngr | 25 ++++++++ src/eval.rs | 18 ++++++ src/eval/primop.rs | 2 +- src/eval/primtype.rs | 5 ++ src/eval/value.rs | 33 ++++++++++ src/syntax/ast.rs | 26 +++++++- src/syntax/eval.rs | 45 +++++++++++++- src/syntax/parser.lalrpop | 113 +++++++++++++++++++++++----------- src/syntax/pretty.rs | 111 ++++++++++++++++++++++++++++++++- src/syntax/tokens.rs | 26 ++++++++ src/syntax/validate.rs | 22 +++++-- src/type_infer/convert.rs | 13 +++- 12 files changed, 389 insertions(+), 50 deletions(-) create mode 100644 examples/basic/broken0001.ngr diff --git a/examples/basic/broken0001.ngr b/examples/basic/broken0001.ngr new file mode 100644 index 0000000..360380a --- /dev/null +++ b/examples/basic/broken0001.ngr @@ -0,0 +1,25 @@ +struct Point { + x: u64; + y: u64; +} + +function getX(p: Point) -> u64 + p.x; + +function getY(p: Point) -> u64 + p.y; + +function newPoint(x, y) -> Point + Point { + x: x; + y: y; + }; + +function slope(p1, p2) -> u64 + (getY(p2) - p1.y) / (getX(p2) - p1.x); + +origin = newPoint(0, 0); +farther = newPoint(4, 4); + +mySlope = slope(origin, farther); +print mySlope; \ No newline at end of file diff --git a/src/eval.rs b/src/eval.rs index 182f640..daa2b4b 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -86,6 +86,14 @@ pub enum EvalError { usize, usize, ), + #[error("Value has no fields {1} (attempt to get field {2} at {0:?})")] + NoFieldForValue(crate::syntax::Location, Value, ArcIntern), + #[error("Bad field {2} for structure {1:?} at {0:?}")] + BadFieldForStructure( + crate::syntax::Location, + Option>, + ArcIntern, + ), } impl PartialEq> for EvalError { @@ -149,6 +157,16 @@ impl PartialEq> for EvalError { EvalError::WrongArgCount(w, x, y, z) => a == w && b == x && c == y && d == z, _ => false, }, + + EvalError::NoFieldForValue(a, b, c) => match other { + EvalError::NoFieldForValue(x, y, z) => a == x && b == y && c == z, + _ => false, + }, + + EvalError::BadFieldForStructure(a, b, c) => match other { + EvalError::BadFieldForStructure(x, y, z) => a == x && b == y && c == z, + _ => false, + }, } } } diff --git a/src/eval/primop.rs b/src/eval/primop.rs index 322459f..aeecb57 100644 --- a/src/eval/primop.rs +++ b/src/eval/primop.rs @@ -192,7 +192,7 @@ impl Value { right.clone(), )), }, - Value::Closure(_, _, _, _) | Value::Void => { + Value::Closure(_, _, _, _) | Value::Structure(_, _) | Value::Void => { Err(PrimOpError::BadTypeFor(operation.to_string(), left.clone())) } } diff --git a/src/eval/primtype.rs b/src/eval/primtype.rs index f67768e..0d9abc6 100644 --- a/src/eval/primtype.rs +++ b/src/eval/primtype.rs @@ -46,6 +46,8 @@ impl Display for PrimitiveType { pub enum ValuePrimitiveTypeError { #[error("Could not convert function value to primitive type (possible function name: {0:?}")] CannotConvertFunction(Option), + #[error("Could not convert structure value to primitive type (possible function name: {0:?}")] + CannotConvertStructure(Option), } impl<'a, IR> TryFrom<&'a Value> for PrimitiveType { @@ -67,6 +69,9 @@ impl<'a, IR> TryFrom<&'a Value> for PrimitiveType { Value::Closure(name, _, _, _) => Err(ValuePrimitiveTypeError::CannotConvertFunction( name.as_ref().map(|x| (**x).clone()), )), + Value::Structure(name, _) => Err(ValuePrimitiveTypeError::CannotConvertStructure( + name.as_ref().map(|x| (**x).clone()), + )), } } } diff --git a/src/eval/value.rs b/src/eval/value.rs index e3b8230..da54910 100644 --- a/src/eval/value.rs +++ b/src/eval/value.rs @@ -1,5 +1,6 @@ use crate::util::scoped_map::ScopedMap; use internment::ArcIntern; +use std::collections::HashMap; use std::fmt; /// Values in the interpreter. @@ -26,6 +27,10 @@ pub enum Value { Vec>, IR, ), + Structure( + Option>, + HashMap, Value>, + ), } impl Value { @@ -50,6 +55,10 @@ impl Value { let new_env = env.clone().map_values(|x| x.strip()); Value::Closure(name.clone(), new_env, args.clone(), ()) } + Value::Structure(name, fields) => Value::Structure( + name.clone(), + fields.iter().map(|(n, v)| (n.clone(), v.strip())).collect(), + ), } } } @@ -68,6 +77,16 @@ fn format_value(value: &Value, f: &mut fmt::Formatter<'_>) -> fmt::Resul Value::Number(x) => write!(f, "{}", x), Value::Closure(Some(name), _, _, _) => write!(f, "", name), Value::Closure(None, _, _, _) => write!(f, ""), + Value::Structure(on, fields) => { + if let Some(n) = on { + write!(f, "{}", n.as_str())?; + } + write!(f, "{{")?; + for (n, v) in fields.iter() { + write!(f, " {}: {},", n, v)?; + } + write!(f, " }}") + } } } @@ -132,6 +151,20 @@ impl PartialEq> for Value { _ => false, }, Value::Closure(_, _, _, _) => false, + Value::Structure(on1, fields1) => match other { + Value::Structure(on2, fields2) => { + on1 == on2 && { + let left = fields1.keys().all(|x| fields2.contains_key(x)); + let right = fields2.keys().all(|x| fields1.contains_key(x)); + + left && right + && fields1 + .iter() + .all(|(k, v)| fields2.get(k).map(|v2| v == v2).unwrap_or(false)) + } + } + _ => false, + }, } } } diff --git a/src/syntax/ast.rs b/src/syntax/ast.rs index 5a947fe..92733df 100644 --- a/src/syntax/ast.rs +++ b/src/syntax/ast.rs @@ -27,7 +27,13 @@ pub struct Program { #[derive(Clone, Debug, PartialEq)] pub enum TopLevel { Statement(Statement), - Function(Option, Vec, Expression), + Function( + Option, + Vec<(Name, Option)>, + Option, + Expression, + ), + Structure(Location, Option, Vec<(Name, Type)>), } /// A Name. @@ -126,7 +132,9 @@ impl PartialEq for Statement { #[derive(Clone, Debug)] pub enum Expression { Value(Location, Value), + Constructor(Location, Name, Vec<(Name, Expression)>), Reference(Location, String), + FieldRef(Location, Box, Name), Cast(Location, String, Box), Primitive(Location, String, Vec), Call(Location, Box, Vec), @@ -140,10 +148,18 @@ impl PartialEq for Expression { Expression::Value(_, val2) => val1 == val2, _ => false, }, + Expression::Constructor(_, name1, fields1) => match other { + Expression::Constructor(_, name2, fields2) => name1 == name2 && fields1 == fields2, + _ => false, + }, Expression::Reference(_, var1) => match other { Expression::Reference(_, var2) => var1 == var2, _ => false, }, + Expression::FieldRef(_, exp1, field1) => match other { + Expression::FieldRef(_, exp2, field2) => exp1 == exp2 && field1 == field2, + _ => false, + }, Expression::Cast(_, t1, e1) => match other { Expression::Cast(_, t2, e2) => t1 == t2 && e1 == e2, _ => false, @@ -169,7 +185,9 @@ impl Expression { pub fn location(&self) -> &Location { match self { Expression::Value(loc, _) => loc, + Expression::Constructor(loc, _, _) => loc, Expression::Reference(loc, _) => loc, + Expression::FieldRef(loc, _, _) => loc, Expression::Cast(loc, _, _) => loc, Expression::Primitive(loc, _, _) => loc, Expression::Call(loc, _, _) => loc, @@ -190,3 +208,9 @@ pub enum Value { /// number at a later time. Number(Option, Option, u64), } + +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum Type { + Named(Name), + Struct(Option, Vec<(Option, Option)>), +} diff --git a/src/syntax/eval.rs b/src/syntax/eval.rs index 98f482c..506ed13 100644 --- a/src/syntax/eval.rs +++ b/src/syntax/eval.rs @@ -2,6 +2,7 @@ use crate::eval::{EvalError, PrimitiveType, Value}; use crate::syntax::{ConstantType, Expression, Name, Program, Statement, TopLevel}; use crate::util::scoped_map::ScopedMap; use internment::ArcIntern; +use std::collections::HashMap; use std::str::FromStr; impl Program { @@ -23,11 +24,15 @@ impl Program { for stmt in self.items.iter() { match stmt { - TopLevel::Function(name, arg_names, body) => { + TopLevel::Function(name, arg_names, _, body) => { last_result = Value::Closure( name.clone().map(Name::intern), env.clone(), - arg_names.iter().cloned().map(Name::intern).collect(), + arg_names + .iter() + .cloned() + .map(|(x, _)| Name::intern(x)) + .collect(), body.clone(), ); if let Some(name) = name { @@ -36,6 +41,10 @@ impl Program { } TopLevel::Statement(stmt) => last_result = stmt.eval(&mut stdout, &mut env)?, + + TopLevel::Structure(_, _, _) => { + last_result = Value::Void; + } } } @@ -98,11 +107,43 @@ impl Expression { }, }, + Expression::Constructor(_, on, fields) => { + let mut map = HashMap::with_capacity(fields.len()); + + for (k, v) in fields.iter() { + map.insert(k.clone().intern(), v.eval(stdout, env)?); + } + + Ok(Value::Structure(Some(on.clone().intern()), map)) + } + Expression::Reference(loc, n) => env .get(&ArcIntern::new(n.clone())) .ok_or_else(|| EvalError::LookupFailed(loc.clone(), n.clone())) .cloned(), + Expression::FieldRef(loc, expr, field) => { + let struck = expr.eval(stdout, env)?; + + if let Value::Structure(on, mut fields) = struck { + if let Some(value) = fields.remove(&field.clone().intern()) { + Ok(value) + } else { + Err(EvalError::BadFieldForStructure( + loc.clone(), + on, + field.clone().intern(), + )) + } + } else { + Err(EvalError::NoFieldForValue( + loc.clone(), + struck, + field.clone().intern(), + )) + } + } + Expression::Cast(_, target, expr) => { let target_type = PrimitiveType::from_str(target)?; let value = expr.eval(stdout, env)?; diff --git a/src/syntax/parser.lalrpop b/src/syntax/parser.lalrpop index ee546b9..e93e29f 100644 --- a/src/syntax/parser.lalrpop +++ b/src/syntax/parser.lalrpop @@ -9,7 +9,7 @@ //! eventually want to leave lalrpop behind.) //! use crate::syntax::{Location, ParserError}; -use crate::syntax::ast::{Program,TopLevel,Statement,Expression,Value,Name}; +use crate::syntax::ast::{Program,TopLevel,Statement,Expression,Value,Name,Type}; use crate::syntax::tokens::{ConstantType, Token}; use internment::ArcIntern; @@ -29,16 +29,21 @@ extern { // here we redeclare all of the tokens. enum Token { "=" => Token::Equals, + ":" => Token::Colon, ";" => Token::Semi, "," => Token::Comma, + "." => Token::Dot, "(" => Token::LeftParen, ")" => Token::RightParen, "<" => Token::LessThan, ">" => Token::GreaterThan, + "_" => Token::Underscore, "{" => Token::OpenBrace, "}" => Token::CloseBrace, + "->" => Token::SingleArrow, "function" => Token::Function, + "struct" => Token::Struct, "print" => Token::Print, "+" => Token::Operator('+'), @@ -53,6 +58,7 @@ extern { // which is why we put their types in angle brackets. "" => Token::Number((>,>,)), "" => Token::Variable(>), + "" => Token::TypeName(>), } } @@ -74,38 +80,18 @@ ProgramTopLevel: Vec = { pub TopLevel: TopLevel = { => f, + => s, ";" => TopLevel::Statement(s), } Function: TopLevel = { - "function" "(" OptionalComma ")" ";" => - TopLevel::Function(opt_name, args, exp), -// "function" "(" OptionalComma ")" "{" "}" => -// TopLevel::Function(opt_name, args, Expression::Block(Location::new(file_idx, s..e), vec![])), -// "function" "(" OptionalComma ")" "{" "}" => -// TopLevel::Function(opt_name, args, Expression::Block(Location::new(file_idx, s..e), stmts)), + "function" "(" > ")" " Type)?> ";" => + TopLevel::Function(opt_name, args, ret.map(|x| x.1), exp), } -OptionalName: Option = { - "> => - Some(Name::new(v, Location::new(file_idx, name_start..name_end))), - => None, -} - -Arguments: Vec = { - "," => { - args.push(arg); - args - }, - - => vec![arg], - - => Vec::new(), -} - -Argument: Name = { - "> => - Name::new(v, Location::new(file_idx, name_start..name_end)), +Argument: (Name, Option) = { + "> => + (Name::new(v, Location::new(file_idx, name_start..name_end)), t.map(|v| v.1)), } OptionalComma: () = { @@ -113,6 +99,40 @@ OptionalComma: () = { "," => (), } +Structure: TopLevel = { + "struct" "{" "}" => { + TopLevel::Structure(Location::new(file_idx, s..e), on, fields) + } +} + +Field: (Name, Type) = { + "> ":" ";" => + (Name::new(name, Location::new(file_idx, s..e)), field_type) +} + +Type: Type = { + => Type::Named(name), + => Type::Named(t), + "struct" "{" "}" => + Type::Struct(on, fields), +} + +TypeField: (Option, Option) = { + ":" ";" => (Some(name), Some(ty)), + (":" "_")? ";" => (Some(name), None), + "_" ":" ";" => (None, Some(ty)), +} + +Name: Name = { + "> => + Name::new(v, Location::new(file_idx, name_start..name_end)), +} + +TypeName: Name = { + "> => + Name::new(v, Location::new(file_idx, name_start..name_end)), +} + Statements: Vec = { // a statement is either a set of statements followed by another // statement (note, here, that you can name the result of a sub-parse @@ -175,9 +195,19 @@ Statement: Statement = { // parse something like "1 + 2 * 3", for example, versus "1 + 2 + 3" or // "1 * 2 + 3", and hopefully that'll help. Expression: Expression = { + ConstructorExpression, AdditiveExpression, } +ConstructorExpression: Expression = { + "{" "}" => + Expression::Constructor(Location::new(file_idx, s..e), name, fields), +} + +FieldSetter: (Name, Expression) = { + ":" ";" => (name, expr), +} + // we group addition and subtraction under the heading "additive" AdditiveExpression: Expression = { "+" => @@ -205,18 +235,15 @@ UnaryExpression: Expression = { } CallExpression: Expression = { - "(" ")" => + "(" > ")" => Expression::Call(Location::new(file_idx, s..e), Box::new(f), args), - AtomicExpression, + FieldExpression, } -CallArguments: Vec = { - => vec![], - => vec![e], - "," => { - args.push(e); - args - } +FieldExpression: Expression = { + "." => + Expression::FieldRef(Location::new(file_idx, s..e), Box::new(fe), field), + AtomicExpression, } // finally, we describe our lowest-level expressions as "atomic", because @@ -232,4 +259,16 @@ AtomicExpression: Expression = { // finally, let people parenthesize expressions and get back to a // lower precedence "(" ")" => e, -} \ No newline at end of file +} + +// Lifted from the LALRPop book, a comma-separated list of T that may or +// may not conclude with a comma. +Comma: Vec = { + ",")*> => match e { + None => v, + Some(e) => { + v.push(e); + v + } + } +}; \ No newline at end of file diff --git a/src/syntax/pretty.rs b/src/syntax/pretty.rs index 740021e..ea306b6 100644 --- a/src/syntax/pretty.rs +++ b/src/syntax/pretty.rs @@ -1,4 +1,4 @@ -use crate::syntax::ast::{ConstantType, Expression, Program, Statement, TopLevel, Value}; +use crate::syntax::ast::{ConstantType, Expression, Program, Statement, TopLevel, Type, Value}; use crate::util::pretty::{derived_display, Allocator}; use pretty::{DocAllocator, DocBuilder}; @@ -21,7 +21,7 @@ impl TopLevel { pub fn pretty<'a>(&self, allocator: &'a Allocator<'a>) -> DocBuilder<'a, Allocator<'a>> { match self { TopLevel::Statement(stmt) => stmt.pretty(allocator), - TopLevel::Function(name, arg_names, body) => allocator + TopLevel::Function(name, args, rettype, body) => allocator .text("function") .append(allocator.space()) .append( @@ -32,13 +32,61 @@ impl TopLevel { .append( allocator .intersperse( - arg_names.iter().map(|x| allocator.text(x.to_string())), + args.iter().map(|(x, t)| { + allocator.text(x.to_string()).append( + t.as_ref() + .map(|t| { + allocator + .text(":") + .append(allocator.space()) + .append(t.pretty(allocator)) + }) + .unwrap_or_else(|| allocator.nil()), + ) + }), allocator.text(","), ) .parens(), ) + .append( + rettype + .as_ref() + .map(|rettype| { + allocator + .space() + .append(allocator.text("->")) + .append(allocator.space()) + .append(rettype.pretty(allocator)) + }) + .unwrap_or_else(|| allocator.nil()), + ) .append(allocator.space()) .append(body.pretty(allocator)), + TopLevel::Structure(_, name, fields) => allocator + .text("struct") + .append(allocator.space()) + .append( + name.as_ref() + .map(|x| allocator.text(x.to_string())) + .unwrap_or_else(|| allocator.nil()), + ) + .append(allocator.space()) + .append(allocator.text("{")) + .append(allocator.hardline()) + .append( + allocator + .concat(fields.iter().map(|(name, ty)| { + allocator + .text(name.to_string()) + .append(allocator.text(":")) + .append(allocator.space()) + .append(ty.pretty(allocator)) + .append(allocator.text(";")) + .append(allocator.hardline()) + })) + .nest(2), + ) + .append(allocator.text("}")), } } } @@ -65,7 +113,28 @@ impl Expression { pub fn pretty<'a>(&self, allocator: &'a Allocator<'a>) -> DocBuilder<'a, Allocator<'a>> { match self { Expression::Value(_, val) => val.pretty(allocator), + Expression::Constructor(_, name, fields) => allocator + .text(name.to_string()) + .append(allocator.space()) + .append( + allocator + .concat(fields.iter().map(|(n, e)| { + allocator + .text(n.to_string()) + .append(allocator.text(":")) + .append(allocator.space()) + .append(e.pretty(allocator)) + .append(allocator.text(";")) + .append(allocator.hardline()) + })) + .nest(2) + .braces(), + ), Expression::Reference(_, var) => allocator.text(var.to_string()), + Expression::FieldRef(_, val, field) => val + .pretty(allocator) + .append(allocator.text(".")) + .append(allocator.text(field.to_string())), Expression::Cast(_, t, e) => allocator .text(t.clone()) .angles() @@ -151,6 +220,42 @@ fn type_suffix(x: &Option) -> &'static str { } } +impl Type { + pub fn pretty<'a>(&self, allocator: &'a Allocator<'a>) -> DocBuilder<'a, Allocator<'a>> { + match self { + Type::Named(x) => allocator.text(x.to_string()), + Type::Struct(name, fields) => allocator + .text("struct") + .append(allocator.space()) + .append( + name.as_ref() + .map(|x| allocator.text(x.to_string())) + .unwrap_or_else(|| allocator.nil()), + ) + .append(allocator.intersperse( + fields.iter().map(|(name, ty)| { + allocator + .text( + name.as_ref() + .map(|x| x.to_string()) + .unwrap_or_else(|| "_".to_string()), + ) + .append(allocator.text(":")) + .append(allocator.space()) + .append( + ty.as_ref() + .map(|x| x.pretty(allocator)) + .unwrap_or_else(|| allocator.text("_")), + ) + .append(allocator.text(";")) + }), + allocator.hardline(), + )) + .braces(), + } + } +} + derived_display!(Program); derived_display!(TopLevel); derived_display!(Statement); diff --git a/src/syntax/tokens.rs b/src/syntax/tokens.rs index 0b0e486..fb139d9 100644 --- a/src/syntax/tokens.rs +++ b/src/syntax/tokens.rs @@ -36,12 +36,18 @@ pub enum Token { #[token("=")] Equals, + #[token(":")] + Colon, + #[token(";")] Semi, #[token(",")] Comma, + #[token(".")] + Dot, + #[token("(")] LeftParen, @@ -54,17 +60,26 @@ pub enum Token { #[token(">")] GreaterThan, + #[token("_")] + Underscore, + #[token("{")] OpenBrace, #[token("}")] CloseBrace, + #[token("->")] + SingleArrow, + #[token("λ")] #[token("lambda")] #[token("function")] Function, + #[token("struct")] + Struct, + // Next we take of any reserved words; I always like to put // these before we start recognizing more complicated regular // expressions. I don't think it matters, but it works for me. @@ -93,21 +108,31 @@ pub enum Token { // letter, too. #[regex(r"[a-z][a-zA-Z0-9_]*", |v| ArcIntern::new(v.slice().to_string()))] Variable(ArcIntern), + + // Type names; these are like variables, but must start with a capital + // letter. + #[regex(r"[A-Z][a-zA-Z0-9_]*", |v| ArcIntern::new(v.slice().to_string()))] + TypeName(ArcIntern), } impl fmt::Display for Token { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Token::Equals => write!(f, "'='"), + Token::Colon => write!(f, "':'"), Token::Semi => write!(f, "';'"), Token::Comma => write!(f, "','"), + Token::Dot => write!(f, "'.'"), Token::LeftParen => write!(f, "'('"), Token::RightParen => write!(f, "')'"), Token::LessThan => write!(f, "<"), Token::GreaterThan => write!(f, ">"), + Token::Underscore => write!(f, "_"), Token::OpenBrace => write!(f, "{{"), Token::CloseBrace => write!(f, "}}"), + Token::SingleArrow => write!(f, "->"), Token::Function => write!(f, "function"), + Token::Struct => write!(f, "struct"), Token::Print => write!(f, "'print'"), Token::Operator(c) => write!(f, "'{}'", c), Token::Number((None, otype, v)) => write!(f, "'{}{}'", v, display_optional_type(otype)), @@ -131,6 +156,7 @@ impl fmt::Display for Token { ) } Token::Variable(s) => write!(f, "'{}'", s), + Token::TypeName(s) => write!(f, "'{}'", s), } } } diff --git a/src/syntax/validate.rs b/src/syntax/validate.rs index 9013ac4..d751578 100644 --- a/src/syntax/validate.rs +++ b/src/syntax/validate.rs @@ -68,7 +68,7 @@ impl Program { /// actually a problem. pub fn validate(&self) -> (Vec, Vec) { let mut bound_variables = ScopedMap::new(); - println!("validate: {}", self); + println!("validate:\n{}", self); self.validate_with_bindings(&mut bound_variables) } @@ -85,7 +85,7 @@ impl Program { let mut warnings = vec![]; for stmt in self.items.iter() { - if let TopLevel::Function(Some(name), _, _) = stmt { + if let TopLevel::Function(Some(name), _, _, _) = stmt { bound_variables.insert(name.to_string(), name.location.clone()); } let (mut new_errors, mut new_warnings) = stmt.validate_with_bindings(bound_variables); @@ -120,12 +120,12 @@ impl TopLevel { bound_variables: &mut ScopedMap, ) -> (Vec, Vec) { match self { - TopLevel::Function(name, arguments, body) => { + TopLevel::Function(name, arguments, _, body) => { bound_variables.new_scope(); if let Some(name) = name { bound_variables.insert(name.name.clone(), name.location.clone()); } - for arg in arguments.iter() { + for (arg, _) in arguments.iter() { bound_variables.insert(arg.name.clone(), arg.location.clone()); } let result = body.validate(bound_variables); @@ -133,6 +133,7 @@ impl TopLevel { result } TopLevel::Statement(stmt) => stmt.validate(bound_variables), + TopLevel::Structure(_, _, _) => (vec![], vec![]), } } } @@ -198,11 +199,24 @@ impl Expression { ) -> (Vec, Vec) { match self { Expression::Value(_, _) => (vec![], vec![]), + Expression::Constructor(_, _, fields) => { + let mut errors = vec![]; + let mut warnings = vec![]; + + for (_, expr) in fields.iter() { + let (mut e, mut w) = expr.validate(variable_map); + errors.append(&mut e); + warnings.append(&mut w); + } + + (errors, warnings) + } Expression::Reference(_, var) if variable_map.contains_key(var) => (vec![], vec![]), Expression::Reference(loc, var) => ( vec![Error::UnboundVariable(loc.clone(), var.clone())], vec![], ), + Expression::FieldRef(_, exp, _) => exp.validate(variable_map), Expression::Cast(location, t, expr) => { let (mut errs, warns) = expr.validate(variable_map); diff --git a/src/type_infer/convert.rs b/src/type_infer/convert.rs index 01b6955..5e004d8 100644 --- a/src/type_infer/convert.rs +++ b/src/type_infer/convert.rs @@ -44,7 +44,7 @@ pub fn convert_top_level( bindings: &mut HashMap, ir::TypeOrVar>, ) -> ir::TopLevel { match top_level { - syntax::TopLevel::Function(name, args, expr) => { + syntax::TopLevel::Function(name, args, _, expr) => { // First, at some point we're going to want to know a location for this function, // which should either be the name if we have one, or the body if we don't. let function_location = match name { @@ -76,7 +76,7 @@ pub fn convert_top_level( renames.new_scope(); let arginfo = args .iter() - .map(|name| { + .map(|(name, _)| { let new_type = ir::TypeOrVar::new(); constraint_db.push(Constraint::IsSomething( name.location.clone(), @@ -84,6 +84,7 @@ pub fn convert_top_level( )); let new_name = finalize_name(bindings, renames, name.clone()); bindings.insert(new_name.clone(), new_type.clone()); + unimplemented!(); (new_name, new_type) }) .collect::>(); @@ -119,6 +120,10 @@ pub fn convert_top_level( syntax::TopLevel::Statement(stmt) => { ir::TopLevel::Statement(convert_statement(stmt, constraint_db, renames, bindings)) } + + syntax::TopLevel::Structure(loc, oname, fields) => { + unimplemented!() + } } } @@ -253,6 +258,8 @@ fn convert_expression( } }, + syntax::Expression::Constructor(_, _, _) => unimplemented!(), + syntax::Expression::Reference(loc, name) => { let iname = ArcIntern::new(name); let final_name = renames.get(&iname).cloned().unwrap_or(iname); @@ -266,6 +273,8 @@ fn convert_expression( (refexp, rtype) } + syntax::Expression::FieldRef(_, _, _) => unimplemented!(), + syntax::Expression::Cast(loc, target, expr) => { let (nexpr, etype) = convert_expression(*expr, constraint_db, renames, bindings); let (prereqs, val_or_ref) = simplify_expr(nexpr);