Broken structure now gets through syntax evaluator.
This commit is contained in:
18
src/eval.rs
18
src/eval.rs
@@ -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,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()),
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>)>),
|
||||
}
|
||||
|
||||
@@ -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)?;
|
||||
|
||||
@@ -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
|
||||
@@ -232,4 +259,16 @@ AtomicExpression: Expression = {
|
||||
// finally, let people parenthesize expressions and get back to a
|
||||
// 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
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -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);
|
||||
|
||||
@@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user