λ Support functions! #5

Open
acw wants to merge 59 commits from awick/functions into develop
12 changed files with 389 additions and 50 deletions
Showing only changes of commit b0cc2fc26b - Show all commits

View File

@@ -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;

View File

@@ -86,6 +86,14 @@ pub enum EvalError<IR> {
usize,
usize,
),
#[error("Value has no fields {1} (attempt to get field {2} at {0:?})")]
NoFieldForValue(crate::syntax::Location, Value<IR>, ArcIntern<String>),
#[error("Bad field {2} for structure {1:?} at {0:?}")]
BadFieldForStructure(
crate::syntax::Location,
Option<ArcIntern<String>>,
ArcIntern<String>,
),
}
impl<IR1: Clone, IR2: Clone> PartialEq<EvalError<IR1>> for EvalError<IR2> {
@@ -149,6 +157,16 @@ impl<IR1: Clone, IR2: Clone> PartialEq<EvalError<IR1>> for EvalError<IR2> {
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,
},
}
}
}

View File

@@ -192,7 +192,7 @@ impl<IR: Clone> Value<IR> {
right.clone(),
)),
},
Value::Closure(_, _, _, _) | Value::Void => {
Value::Closure(_, _, _, _) | Value::Structure(_, _) | Value::Void => {
Err(PrimOpError::BadTypeFor(operation.to_string(), left.clone()))
}
}

View File

@@ -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<String>),
#[error("Could not convert structure value to primitive type (possible function name: {0:?}")]
CannotConvertStructure(Option<String>),
}
impl<'a, IR> TryFrom<&'a Value<IR>> for PrimitiveType {
@@ -67,6 +69,9 @@ impl<'a, IR> TryFrom<&'a Value<IR>> 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()),
)),
}
}
}

View File

@@ -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<IR> {
Vec<ArcIntern<String>>,
IR,
),
Structure(
Option<ArcIntern<String>>,
HashMap<ArcIntern<String>, Value<IR>>,
),
}
impl<IR: Clone> Value<IR> {
@@ -50,6 +55,10 @@ impl<IR: Clone> Value<IR> {
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<IR>(value: &Value<IR>, f: &mut fmt::Formatter<'_>) -> fmt::Resul
Value::Number(x) => write!(f, "{}", x),
Value::Closure(Some(name), _, _, _) => write!(f, "<function {}>", name),
Value::Closure(None, _, _, _) => write!(f, "<function>"),
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<IR1, IR2> PartialEq<Value<IR2>> for Value<IR1> {
_ => 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,
},
}
}
}

View File

@@ -27,7 +27,13 @@ pub struct Program {
#[derive(Clone, Debug, PartialEq)]
pub enum TopLevel {
Statement(Statement),
Function(Option<Name>, Vec<Name>, Expression),
Function(
Option<Name>,
Vec<(Name, Option<Type>)>,
Option<Type>,
Expression,
),
Structure(Location, Option<Name>, 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<Expression>, Name),
Cast(Location, String, Box<Expression>),
Primitive(Location, String, Vec<Expression>),
Call(Location, Box<Expression>, Vec<Expression>),
@@ -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<u8>, Option<ConstantType>, u64),
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Type {
Named(Name),
Struct(Option<Name>, Vec<(Option<Name>, Option<Type>)>),
}

View File

@@ -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)?;

View File

@@ -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.
"<num>" => Token::Number((<Option<u8>>,<Option<ConstantType>>,<u64>)),
"<var>" => Token::Variable(<ArcIntern<String>>),
"<type>" => Token::TypeName(<ArcIntern<String>>),
}
}
@@ -74,38 +80,18 @@ ProgramTopLevel: Vec<TopLevel> = {
pub TopLevel: TopLevel = {
<f:Function> => f,
<s:Structure> => s,
<s:Statement> ";" => TopLevel::Statement(s),
}
Function: TopLevel = {
"function" <opt_name:OptionalName> "(" <args:Arguments> OptionalComma ")" <exp:Expression> ";" =>
TopLevel::Function(opt_name, args, exp),
// "function" <opt_name:OptionalName> "(" <args:Arguments> OptionalComma ")" <s:@L> "{" "}" <e:@L> =>
// TopLevel::Function(opt_name, args, Expression::Block(Location::new(file_idx, s..e), vec![])),
// "function" <opt_name:OptionalName> "(" <args:Arguments> OptionalComma ")" <s:@L> "{" <stmts:Statements> "}" <e:@L> =>
// TopLevel::Function(opt_name, args, Expression::Block(Location::new(file_idx, s..e), stmts)),
"function" <opt_name:Name?> "(" <args:Comma<Argument>> ")" <ret:("->" Type)?> <exp:Expression> ";" =>
TopLevel::Function(opt_name, args, ret.map(|x| x.1), exp),
}
OptionalName: Option<Name> = {
<name_start: @L> <v:"<var>"> <name_end: @L> =>
Some(Name::new(v, Location::new(file_idx, name_start..name_end))),
=> None,
}
Arguments: Vec<Name> = {
<mut args:Arguments> "," <arg:Argument> => {
args.push(arg);
args
},
<arg:Argument> => vec![arg],
=> Vec::new(),
}
Argument: Name = {
<name_start: @L> <v:"<var>"> <name_end: @L> =>
Name::new(v, Location::new(file_idx, name_start..name_end)),
Argument: (Name, Option<Type>) = {
<name_start: @L> <v:"<var>"> <name_end: @L> <t:(":" Type)?> =>
(Name::new(v, Location::new(file_idx, name_start..name_end)), t.map(|v| v.1)),
}
OptionalComma: () = {
@@ -113,6 +99,40 @@ OptionalComma: () = {
"," => (),
}
Structure: TopLevel = {
<s:@L> "struct" <on: TypeName?> "{" <fields: Field*> "}" <e:@L> => {
TopLevel::Structure(Location::new(file_idx, s..e), on, fields)
}
}
Field: (Name, Type) = {
<s:@L> <name:"<var>"> <e:@L> ":" <field_type: Type> ";" =>
(Name::new(name, Location::new(file_idx, s..e)), field_type)
}
Type: Type = {
<name:Name> => Type::Named(name),
<t:TypeName> => Type::Named(t),
"struct" <on: TypeName?> "{" <fields: TypeField*> "}" =>
Type::Struct(on, fields),
}
TypeField: (Option<Name>, Option<Type>) = {
<name: Name> ":" <ty: Type> ";" => (Some(name), Some(ty)),
<name: Name> (":" "_")? ";" => (Some(name), None),
"_" ":" <ty: Type> ";" => (None, Some(ty)),
}
Name: Name = {
<name_start: @L> <v:"<var>"> <name_end: @L> =>
Name::new(v, Location::new(file_idx, name_start..name_end)),
}
TypeName: Name = {
<name_start: @L> <v:"<type>"> <name_end: @L> =>
Name::new(v, Location::new(file_idx, name_start..name_end)),
}
Statements: Vec<Statement> = {
// 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 = {
<s:@L> <name:TypeName> "{" <fields:FieldSetter*> "}" <e:@L> =>
Expression::Constructor(Location::new(file_idx, s..e), name, fields),
}
FieldSetter: (Name, Expression) = {
<name:Name> ":" <expr:Expression> ";" => (name, expr),
}
// we group addition and subtraction under the heading "additive"
AdditiveExpression: Expression = {
<ls: @L> <e1:AdditiveExpression> <l: @L> "+" <e2:MultiplicativeExpression> <le: @L> =>
@@ -205,18 +235,15 @@ UnaryExpression: Expression = {
}
CallExpression: Expression = {
<s: @L> <f:CallExpression> "(" <args: CallArguments> ")" <e: @L> =>
<s: @L> <f:CallExpression> "(" <args: Comma<Expression>> ")" <e: @L> =>
Expression::Call(Location::new(file_idx, s..e), Box::new(f), args),
AtomicExpression,
FieldExpression,
}
CallArguments: Vec<Expression> = {
=> vec![],
<e:Expression> => vec![e],
<mut args:CallArguments> "," <e:Expression> => {
args.push(e);
args
}
FieldExpression: Expression = {
<s: @L> <fe:FieldExpression> "." <field:Name> <e: @L> =>
Expression::FieldRef(Location::new(file_idx, s..e), Box::new(fe), field),
AtomicExpression,
}
// finally, we describe our lowest-level expressions as "atomic", because
@@ -233,3 +260,15 @@ AtomicExpression: Expression = {
// lower precedence
"(" <e:Expression> ")" => e,
}
// Lifted from the LALRPop book, a comma-separated list of T that may or
// may not conclude with a comma.
Comma<T>: Vec<T> = {
<mut v:(<T> ",")*> <e:T?> => match e {
None => v,
Some(e) => {
v.push(e);
v
}
}
};

View File

@@ -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<ConstantType>) -> &'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);

View File

@@ -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<String>),
// 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<String>),
}
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),
}
}
}

View File

@@ -68,7 +68,7 @@ impl Program {
/// actually a problem.
pub fn validate(&self) -> (Vec<Error>, Vec<Warning>) {
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<String, Location>,
) -> (Vec<Error>, Vec<Warning>) {
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<Error>, Vec<Warning>) {
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);

View File

@@ -44,7 +44,7 @@ pub fn convert_top_level(
bindings: &mut HashMap<ArcIntern<String>, ir::TypeOrVar>,
) -> ir::TopLevel<ir::TypeOrVar> {
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::<Vec<_>>();
@@ -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);