Broken structure now gets through syntax evaluator.

This commit is contained in:
2024-03-08 18:39:13 -07:00
parent 77c4277625
commit b0cc2fc26b
12 changed files with 389 additions and 50 deletions

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

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