Compare commits

..

2 Commits

Author SHA1 Message Date
Greg Shuflin
d65233240a Unify u32-based identifiers into common code
Create a new type Id<T> paramaterized by whatever specific class of IDs
is relevant to a domain; create stores and macros to support this; and
repace the existing Id types.
2021-10-27 00:36:23 -07:00
Greg Shuflin
a3463f5519 Add rustfmt.toml and format tree walk evaluator:wq 2021-10-27 00:03:15 -07:00
8 changed files with 449 additions and 400 deletions

8
rustfmt.toml Normal file
View File

@ -0,0 +1,8 @@
max_width = 110
use_small_heuristics = "max"
imports_indent = "block"
imports_granularity = "crate"
group_imports = "stdexternalcrate"
match_arm_blocks = false
where_single_line = true

View File

@ -2,7 +2,6 @@
#![allow(clippy::enum_variant_names)] #![allow(clippy::enum_variant_names)]
use std::rc::Rc; use std::rc::Rc;
use std::fmt;
use std::convert::{AsRef, From}; use std::convert::{AsRef, From};
mod visitor; mod visitor;
@ -13,41 +12,19 @@ pub use visitor::*;
use crate::derivative::Derivative; use crate::derivative::Derivative;
use crate::tokenizing::Location; use crate::tokenizing::Location;
use crate::identifier::{Id, define_id_kind};
/// An abstract identifier for an AST node. Note that define_id_kind!(ASTItem);
/// the u32 index limits the size of an AST to 2^32 nodes.
#[derive(Debug, PartialEq, Eq, Hash, Clone, Default)] /*
pub struct ItemId { #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Default)]
pub idx: u32, pub struct ASTItem;
impl IdKind for ASTItem {
fn tag() -> &'static str { "ASTItem" }
} }
*/
impl ItemId { pub type ItemId = Id<ASTItem>;
fn new(n: u32) -> ItemId {
ItemId { idx: n }
}
}
impl fmt::Display for ItemId {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "ItemId:{}", self.idx)
}
}
pub struct ItemIdStore {
last_idx: u32
}
impl ItemIdStore {
pub fn new() -> ItemIdStore {
ItemIdStore { last_idx: 0 }
}
pub fn fresh(&mut self) -> ItemId {
let idx = self.last_idx;
self.last_idx += 1;
ItemId::new(idx)
}
}
#[derive(Derivative, Debug)] #[derive(Derivative, Debug)]
#[derivative(PartialEq)] #[derivative(PartialEq)]

View File

@ -0,0 +1,74 @@
use std::{
fmt::{self, Debug},
hash::Hash,
marker::PhantomData,
};
pub trait IdKind: Debug + Copy + Clone + Hash + PartialEq + Eq + Default {
fn tag() -> &'static str;
}
/// A generalized abstract identifier type of up to 2^32-1 entries.
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, Default)]
pub struct Id<T>
where T: IdKind
{
idx: u32,
t: PhantomData<T>,
}
impl<T> Id<T>
where T: IdKind
{
fn new(n: u32) -> Self {
Self { idx: n, t: PhantomData }
}
#[allow(dead_code)]
pub fn as_u32(&self) -> u32 {
self.idx
}
}
impl<T> fmt::Display for Id<T>
where T: IdKind
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}:{}", self.idx, T::tag())
}
}
pub struct IdStore<T>
where T: IdKind
{
last_idx: u32,
t: PhantomData<T>,
}
impl<T> IdStore<T>
where T: IdKind
{
pub fn new() -> Self {
Self { last_idx: 0, t: PhantomData }
}
pub fn fresh(&mut self) -> Id<T> {
let idx = self.last_idx;
self.last_idx += 1;
Id::new(idx)
}
}
macro_rules! define_id_kind {
($name:ident) => {
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, Default)]
pub struct $name;
impl crate::identifier::IdKind for $name {
fn tag() -> &'static str {
stringify!($name)
}
}
};
}
pub(crate) use define_id_kind;

View File

@ -23,10 +23,10 @@ mod tokenizing;
mod symbol_table; mod symbol_table;
mod builtin; mod builtin;
mod error; mod error;
//mod eval;
//mod reduced_ast;
mod reduced_ir; mod reduced_ir;
mod tree_walk_eval; mod tree_walk_eval;
#[macro_use]
mod identifier;
mod schala; mod schala;

View File

@ -170,6 +170,7 @@ use crate::tokenizing::TokenKind::*;
use crate::tokenizing::Location; use crate::tokenizing::Location;
use crate::ast::*; use crate::ast::*;
use crate::identifier::IdStore;
/// Represents a parsing error /// Represents a parsing error
#[derive(Debug)] #[derive(Debug)]
@ -201,7 +202,7 @@ pub struct Parser {
parse_record: Vec<ParseRecord>, parse_record: Vec<ParseRecord>,
parse_level: u32, parse_level: u32,
restrictions: ParserRestrictions, restrictions: ParserRestrictions,
id_store: ItemIdStore, id_store: IdStore<ASTItem>,
} }
@ -252,7 +253,7 @@ impl Parser {
parse_record: vec![], parse_record: vec![],
parse_level: 0, parse_level: 0,
restrictions: ParserRestrictions { no_struct_literal: false }, restrictions: ParserRestrictions { no_struct_literal: false },
id_store: ItemIdStore::new(), id_store: IdStore::new(),
} }
} }

View File

@ -14,42 +14,10 @@ mod resolver;
mod symbol_trie; mod symbol_trie;
use symbol_trie::SymbolTrie; use symbol_trie::SymbolTrie;
mod test; mod test;
use crate::identifier::{Id, IdStore, define_id_kind};
define_id_kind!(DefItem);
//TODO parameterize different types of ID pub type DefId = Id<DefItem>;
/// ID used for definitions
#[derive(Debug, PartialEq, Eq, Hash, Clone, Default)]
pub struct DefId {
idx: u32,
}
impl DefId {
pub fn as_u32(&self) -> u32 {
self.idx
}
}
impl fmt::Display for DefId {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "DefId:{}", self.idx)
}
}
pub struct DefIdStore {
last_idx: u32
}
impl DefIdStore {
pub fn new() -> Self {
Self { last_idx: 0 }
}
pub fn fresh(&mut self) -> DefId {
let idx = self.last_idx;
self.last_idx += 1;
DefId { idx }
}
}
/// Fully-qualified symbol name /// Fully-qualified symbol name
#[derive(Debug, Clone, Eq, PartialEq, Hash, PartialOrd, Ord)] #[derive(Debug, Clone, Eq, PartialEq, Hash, PartialOrd, Ord)]
@ -170,7 +138,7 @@ impl<K> NameTable<K> {
//cf. p. 150 or so of Language Implementation Patterns //cf. p. 150 or so of Language Implementation Patterns
pub struct SymbolTable { pub struct SymbolTable {
def_id_store: DefIdStore, def_id_store: IdStore<DefItem>,
/// Used for import resolution. /// Used for import resolution.
symbol_trie: SymbolTrie, symbol_trie: SymbolTrie,
@ -190,7 +158,7 @@ pub struct SymbolTable {
impl SymbolTable { impl SymbolTable {
pub fn new() -> SymbolTable { pub fn new() -> SymbolTable {
SymbolTable { SymbolTable {
def_id_store: DefIdStore::new(), def_id_store: IdStore::new(),
symbol_trie: SymbolTrie::new(), symbol_trie: SymbolTrie::new(),
fq_names: NameTable::new(), fq_names: NameTable::new(),
types: NameTable::new(), types: NameTable::new(),

View File

@ -1,12 +1,14 @@
use crate::reduced_ir::{ReducedIR, Expression, Lookup, Callable, FunctionDefinition, Statement, Literal, Alternative, Pattern}; use std::{convert::From, fmt::Write, rc::Rc};
use crate::symbol_table::{DefId};
use crate::util::ScopeStack;
use crate::builtin::Builtin;
use crate::typechecking::TypeId;
use std::fmt::Write; use crate::{
use std::rc::Rc; builtin::Builtin,
use std::convert::From; reduced_ir::{
Alternative, Callable, Expression, FunctionDefinition, Literal, Lookup, Pattern, ReducedIR, Statement,
},
symbol_table::DefId,
typechecking::TypeId,
util::ScopeStack,
};
mod test; mod test;
@ -14,14 +16,14 @@ type EvalResult<T> = Result<T, RuntimeError>;
#[derive(Debug)] #[derive(Debug)]
pub struct State<'a> { pub struct State<'a> {
environments: ScopeStack<'a, Memory, MemoryValue>, environments: ScopeStack<'a, Memory, MemoryValue>,
} }
//TODO - eh, I dunno, maybe it doesn't matter exactly how memory works in the tree-walking //TODO - eh, I dunno, maybe it doesn't matter exactly how memory works in the tree-walking
//evaluator //evaluator
#[derive(Debug, PartialEq, Eq, Hash, Clone)] #[derive(Debug, PartialEq, Eq, Hash, Clone)]
enum Memory { enum Memory {
Index(u32) Index(u32),
} }
// This is for function param lookups, and is a hack // This is for function param lookups, and is a hack
@ -39,22 +41,18 @@ impl From<&DefId> for Memory {
#[derive(Debug)] #[derive(Debug)]
struct RuntimeError { struct RuntimeError {
msg: String msg: String,
} }
impl From<String> for RuntimeError { impl From<String> for RuntimeError {
fn from(msg: String) -> Self { fn from(msg: String) -> Self {
Self { Self { msg }
msg
}
} }
} }
impl From<&str> for RuntimeError { impl From<&str> for RuntimeError {
fn from(msg: &str) -> Self { fn from(msg: &str) -> Self {
Self { Self { msg: msg.to_string() }
msg: msg.to_string(),
}
} }
} }
@ -65,17 +63,17 @@ impl RuntimeError {
} }
} }
fn paren_wrapped(terms: impl Iterator<Item=String>) -> String { fn paren_wrapped(terms: impl Iterator<Item = String>) -> String {
let mut buf = String::new(); let mut buf = String::new();
write!(buf, "(").unwrap(); write!(buf, "(").unwrap();
for term in terms.map(Some).intersperse(None) { for term in terms.map(Some).intersperse(None) {
match term { match term {
Some(e) => write!(buf, "{}", e).unwrap(), Some(e) => write!(buf, "{}", e).unwrap(),
None => write!(buf, ", ").unwrap(), None => write!(buf, ", ").unwrap(),
}; };
} }
write!(buf, ")").unwrap(); write!(buf, ")").unwrap();
buf buf
} }
/// Anything that can be stored in memory; that is, a function definition, or a fully-evaluated /// Anything that can be stored in memory; that is, a function definition, or a fully-evaluated
@ -92,7 +90,6 @@ impl From<Primitive> for MemoryValue {
} }
} }
impl MemoryValue { impl MemoryValue {
fn to_repl(&self) -> String { fn to_repl(&self) -> String {
match self { match self {
@ -126,26 +123,23 @@ enum Primitive {
Tuple(Vec<Primitive>), Tuple(Vec<Primitive>),
Literal(Literal), Literal(Literal),
Callable(Callable), Callable(Callable),
Object { Object { type_id: TypeId, tag: u32, items: Vec<Primitive> },
type_id: TypeId,
tag: u32,
items: Vec<Primitive>
},
} }
impl Primitive { impl Primitive {
fn to_repl(&self) -> String { fn to_repl(&self) -> String {
match self { match self {
Primitive::Object { type_id, items, .. } if items.is_empty() => type_id.local_name().to_string(), Primitive::Object { type_id, items, .. } if items.is_empty() => type_id.local_name().to_string(),
Primitive::Object { type_id, items, .. } => Primitive::Object { type_id, items, .. } => {
format!("{}{}", type_id.local_name(), paren_wrapped(items.iter().map(|item| item.to_repl()))), format!("{}{}", type_id.local_name(), paren_wrapped(items.iter().map(|item| item.to_repl())))
}
Primitive::Literal(lit) => match lit { Primitive::Literal(lit) => match lit {
Literal::Nat(n) => format!("{}", n), Literal::Nat(n) => format!("{}", n),
Literal::Int(i) => format!("{}", i), Literal::Int(i) => format!("{}", i),
Literal::Float(f) => format!("{}", f), Literal::Float(f) => format!("{}", f),
Literal::Bool(b) => format!("{}", b), Literal::Bool(b) => format!("{}", b),
Literal::StringLit(s) => format!("\"{}\"", s), Literal::StringLit(s) => format!("\"{}\"", s),
} },
Primitive::Tuple(terms) => paren_wrapped(terms.iter().map(|x| x.to_repl())), Primitive::Tuple(terms) => paren_wrapped(terms.iter().map(|x| x.to_repl())),
Primitive::Callable(..) => "<some-callable>".to_string(), Primitive::Callable(..) => "<some-callable>".to_string(),
} }
@ -164,9 +158,7 @@ impl From<Literal> for Primitive {
impl<'a> State<'a> { impl<'a> State<'a> {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self { environments: ScopeStack::new(Some("global".to_string())) }
environments: ScopeStack::new(Some("global".to_string()))
}
} }
pub fn evaluate(&mut self, reduced: ReducedIR, repl: bool) -> Vec<Result<String, String>> { pub fn evaluate(&mut self, reduced: ReducedIR, repl: bool) -> Vec<Result<String, String>> {
@ -179,9 +171,7 @@ impl<'a> State<'a> {
for statement in reduced.entrypoint.into_iter() { for statement in reduced.entrypoint.into_iter() {
match self.statement(statement) { match self.statement(statement) {
Ok(Some(output)) if repl => { Ok(Some(output)) if repl => acc.push(Ok(output.to_repl())),
acc.push(Ok(output.to_repl()))
},
Ok(_) => (), Ok(_) => (),
Err(error) => { Err(error) => {
acc.push(Err(error.msg)); acc.push(Err(error.msg));
@ -200,11 +190,7 @@ impl<'a> State<'a> {
ret = Some(prim); ret = Some(prim);
} }
} }
Ok(if let Some(ret) = ret { Ok(if let Some(ret) = ret { ret } else { self.expression(Expression::unit())? })
ret
} else {
self.expression(Expression::unit())?
})
} }
fn statement(&mut self, stmt: Statement) -> EvalResult<Option<MemoryValue>> { fn statement(&mut self, stmt: Statement) -> EvalResult<Option<MemoryValue>> {
@ -214,7 +200,7 @@ impl<'a> State<'a> {
let evaluated = self.expression(expr)?; let evaluated = self.expression(expr)?;
self.environments.insert(id.into(), evaluated.into()); self.environments.insert(id.into(), evaluated.into());
Ok(None) Ok(None)
}, }
Statement::Expression(expr) => { Statement::Expression(expr) => {
let evaluated = self.expression(expr)?; let evaluated = self.expression(expr)?;
Ok(Some(evaluated.into())) Ok(Some(evaluated.into()))
@ -222,252 +208,286 @@ impl<'a> State<'a> {
} }
} }
fn expression(&mut self, expression: Expression) -> EvalResult<Primitive> { fn expression(&mut self, expression: Expression) -> EvalResult<Primitive> {
Ok(match expression { Ok(match expression {
Expression::Literal(lit) => Primitive::Literal(lit), Expression::Literal(lit) => Primitive::Literal(lit),
Expression::Tuple(items) => Primitive::Tuple(items.into_iter().map(|expr| self.expression(expr)).collect::<EvalResult<Vec<Primitive>>>()?), Expression::Tuple(items) => Primitive::Tuple(
Expression::Lookup(kind) => match kind { items
Lookup::Function(ref id) => { .into_iter()
let mem = id.into(); .map(|expr| self.expression(expr))
match self.environments.lookup(&mem) { .collect::<EvalResult<Vec<Primitive>>>()?,
// This just checks that the function exists in "memory" by ID, we don't ),
// actually retrieve it until `apply_function()` Expression::Lookup(kind) => match kind {
Some(MemoryValue::Function(_)) => Primitive::Callable(Callable::UserDefined(id.clone())), Lookup::Function(ref id) => {
x => return Err(format!("Function not found for id: {} : {:?}", id, x).into()), let mem = id.into();
} match self.environments.lookup(&mem) {
}, // This just checks that the function exists in "memory" by ID, we don't
Lookup::Param(n) => { // actually retrieve it until `apply_function()`
let mem = n.into(); Some(MemoryValue::Function(_)) => {
match self.environments.lookup(&mem) { Primitive::Callable(Callable::UserDefined(id.clone()))
Some(MemoryValue::Primitive(prim)) => prim.clone(), }
e => return Err(format!("Param lookup error, got {:?}", e).into()), x => return Err(format!("Function not found for id: {} : {:?}", id, x).into()),
} }
}, }
Lookup::LocalVar(ref id) | Lookup::GlobalVar(ref id) => { Lookup::Param(n) => {
let mem = id.into(); let mem = n.into();
match self.environments.lookup(&mem) { match self.environments.lookup(&mem) {
Some(MemoryValue::Primitive(expr)) => expr.clone(), Some(MemoryValue::Primitive(prim)) => prim.clone(),
_ => return Err(format!("Nothing found for local/gloval variable lookup {}", id).into()), e => return Err(format!("Param lookup error, got {:?}", e).into()),
} }
}, }
}, Lookup::LocalVar(ref id) | Lookup::GlobalVar(ref id) => {
Expression::Assign { ref lval, box rval } => { let mem = id.into();
let mem = lval.into(); match self.environments.lookup(&mem) {
let evaluated = self.expression(rval)?; Some(MemoryValue::Primitive(expr)) => expr.clone(),
self.environments.insert(mem, MemoryValue::Primitive(evaluated)); _ => {
Primitive::unit() return Err(
}, format!("Nothing found for local/gloval variable lookup {}", id).into()
Expression::Call { box f, args } => self.call_expression(f, args)?, )
Expression::Callable(Callable::DataConstructor { type_id, arity, tag }) if arity == 0 => Primitive::Object { }
type_id, tag, items: vec![] }
}, }
Expression::Callable(func) => Primitive::Callable(func), },
Expression::Conditional { box cond, then_clause, else_clause } => { Expression::Assign { ref lval, box rval } => {
let cond = self.expression(cond)?; let mem = lval.into();
match cond { let evaluated = self.expression(rval)?;
Primitive::Literal(Literal::Bool(true)) => self.block(then_clause)?, self.environments.insert(mem, MemoryValue::Primitive(evaluated));
Primitive::Literal(Literal::Bool(false)) => self.block(else_clause)?, Primitive::unit()
v => return Err(format!("Non-boolean value {:?} in if-statement", v).into()) }
} Expression::Call { box f, args } => self.call_expression(f, args)?,
}, Expression::Callable(Callable::DataConstructor { type_id, arity, tag }) if arity == 0 => {
Expression::CaseMatch { box cond, alternatives } => self.case_match_expression(cond, alternatives)?, Primitive::Object { type_id, tag, items: vec![] }
Expression::ReductionError(e) => return Err(e.into()), }
}) Expression::Callable(func) => Primitive::Callable(func),
} Expression::Conditional { box cond, then_clause, else_clause } => {
let cond = self.expression(cond)?;
match cond {
Primitive::Literal(Literal::Bool(true)) => self.block(then_clause)?,
Primitive::Literal(Literal::Bool(false)) => self.block(else_clause)?,
v => return Err(format!("Non-boolean value {:?} in if-statement", v).into()),
}
}
Expression::CaseMatch { box cond, alternatives } => {
self.case_match_expression(cond, alternatives)?
}
Expression::ReductionError(e) => return Err(e.into()),
})
}
fn case_match_expression(&mut self, cond: Expression, alternatives: Vec<Alternative>) -> EvalResult<Primitive> { fn case_match_expression(
fn matches(scrut: &Primitive, pat: &Pattern, scope: &mut ScopeStack<Memory, MemoryValue>) -> bool { &mut self,
match pat { cond: Expression,
Pattern::Ignored => true, alternatives: Vec<Alternative>,
Pattern::Binding(ref def_id) => { ) -> EvalResult<Primitive> {
let mem = def_id.into(); fn matches(scrut: &Primitive, pat: &Pattern, scope: &mut ScopeStack<Memory, MemoryValue>) -> bool {
scope.insert(mem, MemoryValue::Primitive(scrut.clone())); //TODO make sure this doesn't cause problems with nesting match pat {
true Pattern::Ignored => true,
}, Pattern::Binding(ref def_id) => {
Pattern::Literal(pat_literal) => if let Primitive::Literal(scrut_literal) = scrut { let mem = def_id.into();
pat_literal == scrut_literal scope.insert(mem, MemoryValue::Primitive(scrut.clone())); //TODO make sure this doesn't cause problems with nesting
} else { true
false }
}, Pattern::Literal(pat_literal) => {
Pattern::Tuple { subpatterns, tag } => match tag { if let Primitive::Literal(scrut_literal) = scrut {
None => match scrut { pat_literal == scrut_literal
Primitive::Tuple(items) if items.len() == subpatterns.len() => } else {
items.iter().zip(subpatterns.iter()).all(|(item, subpat)| matches(item, subpat, scope)), false
_ => false //TODO should be a type error }
}, }
Some(pattern_tag) => match scrut { Pattern::Tuple { subpatterns, tag } => match tag {
//TODO should test type_ids for runtime type checking, once those work None => match scrut {
Primitive::Object { tag, items, .. } if tag == pattern_tag && items.len() == subpatterns.len() => { Primitive::Tuple(items) if items.len() == subpatterns.len() => items
items.iter().zip(subpatterns.iter()).all(|(item, subpat)| matches(item, subpat, scope)) .iter()
} .zip(subpatterns.iter())
_ => false .all(|(item, subpat)| matches(item, subpat, scope)),
} _ => false, //TODO should be a type error
} },
} Some(pattern_tag) => match scrut {
} //TODO should test type_ids for runtime type checking, once those work
let cond = self.expression(cond)?; Primitive::Object { tag, items, .. }
if tag == pattern_tag && items.len() == subpatterns.len() =>
{
items
.iter()
.zip(subpatterns.iter())
.all(|(item, subpat)| matches(item, subpat, scope))
}
_ => false,
},
},
}
}
let cond = self.expression(cond)?;
for alt in alternatives.into_iter() { for alt in alternatives.into_iter() {
let mut new_scope = self.environments.new_scope(None); let mut new_scope = self.environments.new_scope(None);
if matches(&cond, &alt.pattern, &mut new_scope) { if matches(&cond, &alt.pattern, &mut new_scope) {
let mut new_state = State { let mut new_state = State { environments: new_scope };
environments: new_scope
};
return new_state.block(alt.item) return new_state.block(alt.item);
} }
} }
Err("No valid match in match expression".into()) Err("No valid match in match expression".into())
} }
fn call_expression(&mut self, f: Expression, args: Vec<Expression>) -> EvalResult<Primitive> { fn call_expression(&mut self, f: Expression, args: Vec<Expression>) -> EvalResult<Primitive> {
let func = match self.expression(f)? { let func = match self.expression(f)? {
Primitive::Callable(func) => func, Primitive::Callable(func) => func,
other => return Err(format!("Trying to call non-function value: {:?}", other).into()), other => return Err(format!("Trying to call non-function value: {:?}", other).into()),
}; };
match func { match func {
Callable::Builtin(builtin) => self.apply_builtin(builtin, args), Callable::Builtin(builtin) => self.apply_builtin(builtin, args),
Callable::UserDefined(def_id) => { Callable::UserDefined(def_id) => {
let mem = (&def_id).into(); let mem = (&def_id).into();
match self.environments.lookup(&mem) { match self.environments.lookup(&mem) {
Some(MemoryValue::Function(FunctionDefinition { body })) => { Some(MemoryValue::Function(FunctionDefinition { body })) => {
let body = body.clone(); //TODO ideally this clone would not happen let body = body.clone(); //TODO ideally this clone would not happen
self.apply_function(body, args) self.apply_function(body, args)
}, }
e => Err(format!("Error looking up function with id {}: {:?}", def_id, e).into()) e => Err(format!("Error looking up function with id {}: {:?}", def_id, e).into()),
} }
}, }
Callable::Lambda { arity, body } => { Callable::Lambda { arity, body } => {
if arity as usize != args.len() { if arity as usize != args.len() {
return Err(format!("Lambda expression requries {} arguments, only {} provided", arity, args.len()).into()); return Err(format!(
} "Lambda expression requries {} arguments, only {} provided",
self.apply_function(body, args) arity,
} args.len()
Callable::DataConstructor { type_id, arity, tag } => { )
if arity as usize != args.len() { .into());
return Err(format!("Constructor expression requries {} arguments, only {} provided", arity, args.len()).into()); }
} self.apply_function(body, args)
}
Callable::DataConstructor { type_id, arity, tag } => {
if arity as usize != args.len() {
return Err(format!(
"Constructor expression requries {} arguments, only {} provided",
arity,
args.len()
)
.into());
}
let mut evaluated_args: Vec<Primitive> = vec![]; let mut evaluated_args: Vec<Primitive> = vec![];
for arg in args.into_iter() { for arg in args.into_iter() {
evaluated_args.push(self.expression(arg)?); evaluated_args.push(self.expression(arg)?);
} }
Ok(Primitive::Object { Ok(Primitive::Object { type_id, tag, items: evaluated_args })
type_id, }
tag, Callable::RecordConstructor { type_id: _, tag: _ } => {
items: evaluated_args unimplemented!()
}) }
} }
Callable::RecordConstructor { type_id: _, tag: _ } => { }
unimplemented!()
}
}
}
fn apply_builtin(&mut self, builtin: Builtin, args: Vec<Expression>) -> EvalResult<Primitive> { fn apply_builtin(&mut self, builtin: Builtin, args: Vec<Expression>) -> EvalResult<Primitive> {
use Builtin::*; use Builtin::*;
use Literal::*; use Literal::*;
use Primitive::Literal as Lit; use Primitive::Literal as Lit;
let evaled_args: EvalResult<Vec<Primitive>> = let evaled_args: EvalResult<Vec<Primitive>> =
args.into_iter().map(|arg| self.expression(arg)).collect(); args.into_iter().map(|arg| self.expression(arg)).collect();
let evaled_args = evaled_args?; let evaled_args = evaled_args?;
Ok(match (builtin, evaled_args.as_slice()) { Ok(match (builtin, evaled_args.as_slice()) {
(FieldAccess, /*&[Node::PrimObject { .. }]*/ _) => { (FieldAccess, /*&[Node::PrimObject { .. }]*/ _) => {
return Err("Field access unimplemented".into()); return Err("Field access unimplemented".into());
} }
/* builtin functions */ /* builtin functions */
(IOPrint, &[ref anything]) => { (IOPrint, &[ref anything]) => {
print!("{}", anything.to_repl()); print!("{}", anything.to_repl());
Primitive::Tuple(vec![]) Primitive::Tuple(vec![])
}, }
(IOPrintLn, &[ref anything]) => { (IOPrintLn, &[ref anything]) => {
print!("{}", anything.to_repl()); print!("{}", anything.to_repl());
Primitive::Tuple(vec![]) Primitive::Tuple(vec![])
}, }
(IOGetLine, &[]) => { (IOGetLine, &[]) => {
let mut buf = String::new(); let mut buf = String::new();
std::io::stdin().read_line(&mut buf).expect("Error readling line in 'getline'"); std::io::stdin().read_line(&mut buf).expect("Error readling line in 'getline'");
StringLit(Rc::new(buf.trim().to_string())).into() StringLit(Rc::new(buf.trim().to_string())).into()
}, }
/* Binops */ /* Binops */
(binop, &[ref lhs, ref rhs]) => match (binop, lhs, rhs) { (binop, &[ref lhs, ref rhs]) => match (binop, lhs, rhs) {
// TODO need a better way of handling these literals // TODO need a better way of handling these literals
(Add, Lit(Nat(l)), Lit(Nat(r))) => Nat(l + r).into(), (Add, Lit(Nat(l)), Lit(Nat(r))) => Nat(l + r).into(),
(Add, Lit(Int(l)), Lit(Int(r))) => Int(l + r).into(), (Add, Lit(Int(l)), Lit(Int(r))) => Int(l + r).into(),
(Add, Lit(Nat(l)), Lit(Int(r))) => Int((*l as i64) + (*r as i64)).into(), (Add, Lit(Nat(l)), Lit(Int(r))) => Int((*l as i64) + (*r as i64)).into(),
(Add, Lit(Int(l)), Lit(Nat(r))) => Int((*l as i64) + (*r as i64)).into(), (Add, Lit(Int(l)), Lit(Nat(r))) => Int((*l as i64) + (*r as i64)).into(),
(Concatenate, Lit(StringLit(ref s1)), Lit(StringLit(ref s2))) => StringLit(Rc::new(format!("{}{}", s1, s2))).into(), (Concatenate, Lit(StringLit(ref s1)), Lit(StringLit(ref s2))) => {
(Subtract, Lit(Nat(l)), Lit(Nat(r))) => Nat(l - r).into(), StringLit(Rc::new(format!("{}{}", s1, s2))).into()
(Multiply, Lit(Nat(l)), Lit(Nat(r))) => Nat(l * r).into(), }
(Divide, Lit(Nat(l)), Lit(Nat(r))) => Float((*l as f64)/ (*r as f64)).into(), (Subtract, Lit(Nat(l)), Lit(Nat(r))) => Nat(l - r).into(),
(Quotient, Lit(Nat(l)), Lit(Nat(r))) => if *r == 0 { (Multiply, Lit(Nat(l)), Lit(Nat(r))) => Nat(l * r).into(),
return Err("Divide-by-zero error".into()); (Divide, Lit(Nat(l)), Lit(Nat(r))) => Float((*l as f64) / (*r as f64)).into(),
} else { (Quotient, Lit(Nat(l)), Lit(Nat(r))) => {
Nat(l / r).into() if *r == 0 {
}, return Err("Divide-by-zero error".into());
(Modulo, Lit(Nat(l)), Lit(Nat(r))) => Nat(l % r).into(), } else {
(Exponentiation, Lit(Nat(l)), Lit(Nat(r))) => Nat(l ^ r).into(), Nat(l / r).into()
(BitwiseAnd, Lit(Nat(l)), Lit(Nat(r))) => Nat(l & r).into(), }
(BitwiseOr, Lit(Nat(l)), Lit(Nat(r))) => Nat(l | r).into(), }
(Modulo, Lit(Nat(l)), Lit(Nat(r))) => Nat(l % r).into(),
(Exponentiation, Lit(Nat(l)), Lit(Nat(r))) => Nat(l ^ r).into(),
(BitwiseAnd, Lit(Nat(l)), Lit(Nat(r))) => Nat(l & r).into(),
(BitwiseOr, Lit(Nat(l)), Lit(Nat(r))) => Nat(l | r).into(),
/* comparisons */ /* comparisons */
(Equality, Lit(Nat(l)), Lit(Nat(r))) => Bool(l == r).into(), (Equality, Lit(Nat(l)), Lit(Nat(r))) => Bool(l == r).into(),
(Equality, Lit(Int(l)), Lit(Int(r))) => Bool(l == r).into(), (Equality, Lit(Int(l)), Lit(Int(r))) => Bool(l == r).into(),
(Equality, Lit(Float(l)), Lit(Float(r))) => Bool(l == r).into(), (Equality, Lit(Float(l)), Lit(Float(r))) => Bool(l == r).into(),
(Equality, Lit(Bool(l)), Lit(Bool(r))) => Bool(l == r).into(), (Equality, Lit(Bool(l)), Lit(Bool(r))) => Bool(l == r).into(),
(Equality, Lit(StringLit(ref l)), Lit(StringLit(ref r))) => Bool(l == r).into(), (Equality, 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(Nat(l)), Lit(Nat(r))) => Bool(l < r).into(),
(LessThan, Lit(Int(l)), Lit(Int(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(), (LessThan, Lit(Float(l)), Lit(Float(r))) => Bool(l < r).into(),
(LessThanOrEqual, Lit(Nat(l)), Lit(Nat(r))) => Bool(l <= r).into(), (LessThanOrEqual, Lit(Nat(l)), Lit(Nat(r))) => Bool(l <= r).into(),
(LessThanOrEqual, Lit(Int(l)), Lit(Int(r))) => Bool(l <= r).into(), (LessThanOrEqual, Lit(Int(l)), Lit(Int(r))) => Bool(l <= r).into(),
(LessThanOrEqual, Lit(Float(l)), Lit(Float(r))) => Bool(l <= r).into(), (LessThanOrEqual, Lit(Float(l)), Lit(Float(r))) => Bool(l <= r).into(),
(GreaterThan, Lit(Nat(l)), Lit(Nat(r))) => Bool(l > r).into(), (GreaterThan, Lit(Nat(l)), Lit(Nat(r))) => Bool(l > r).into(),
(GreaterThan, Lit(Int(l)), Lit(Int(r))) => Bool(l > r).into(), (GreaterThan, Lit(Int(l)), Lit(Int(r))) => Bool(l > r).into(),
(GreaterThan, Lit(Float(l)), Lit(Float(r))) => Bool(l > r).into(), (GreaterThan, Lit(Float(l)), Lit(Float(r))) => Bool(l > r).into(),
(GreaterThanOrEqual, Lit(Nat(l)), Lit(Nat(r))) => Bool(l >= r).into(), (GreaterThanOrEqual, Lit(Nat(l)), Lit(Nat(r))) => Bool(l >= r).into(),
(GreaterThanOrEqual, Lit(Int(l)), Lit(Int(r))) => Bool(l >= r).into(), (GreaterThanOrEqual, Lit(Int(l)), Lit(Int(r))) => Bool(l >= r).into(),
(GreaterThanOrEqual, Lit(Float(l)), Lit(Float(r))) => Bool(l >= r).into(), (GreaterThanOrEqual, Lit(Float(l)), Lit(Float(r))) => Bool(l >= r).into(),
(binop, lhs, rhs) => return Err(format!("Invalid binop expression {:?} {:?} {:?}", lhs, binop, rhs).into()), (binop, lhs, rhs) => {
}, return Err(format!("Invalid binop expression {:?} {:?} {:?}", lhs, binop, rhs).into())
(prefix, &[ref arg]) => match (prefix, arg) { }
(BooleanNot, Lit(Bool(true))) => Bool(false), },
(BooleanNot, Lit(Bool(false))) => Bool(true), (prefix, &[ref arg]) => match (prefix, arg) {
(Negate, Lit(Nat(n))) => Int(-(*n as i64)), (BooleanNot, Lit(Bool(true))) => Bool(false),
(Negate, Lit(Int(n))) => Int(-(*n as i64)), (BooleanNot, Lit(Bool(false))) => Bool(true),
(Negate, Lit(Float(f))) => Float(-(*f as f64)), (Negate, Lit(Nat(n))) => Int(-(*n as i64)),
(Increment, Lit(Int(n))) => Int(*n), (Negate, Lit(Int(n))) => Int(-(*n as i64)),
(Increment, Lit(Nat(n))) => Nat(*n), (Negate, Lit(Float(f))) => Float(-(*f as f64)),
_ => return Err("No valid prefix op".into()) (Increment, Lit(Int(n))) => Int(*n),
}.into(), (Increment, Lit(Nat(n))) => Nat(*n),
(x, args) => return Err(format!("bad or unimplemented builtin {:?} | {:?}", x, args).into()), _ => return Err("No valid prefix op".into()),
}) }
} .into(),
(x, args) => return Err(format!("bad or unimplemented builtin {:?} | {:?}", x, args).into()),
})
}
fn apply_function(&mut self, body: Vec<Statement>, args: Vec<Expression>) -> EvalResult<Primitive> { fn apply_function(&mut self, body: Vec<Statement>, args: Vec<Expression>) -> EvalResult<Primitive> {
let mut evaluated_args: Vec<Primitive> = vec![]; let mut evaluated_args: Vec<Primitive> = vec![];
for arg in args.into_iter() { for arg in args.into_iter() {
evaluated_args.push(self.expression(arg)?); evaluated_args.push(self.expression(arg)?);
} }
let mut frame_state = State { let mut frame_state = State { environments: self.environments.new_scope(None) };
environments: self.environments.new_scope(None)
};
for (n, evaled) in evaluated_args.into_iter().enumerate() { for (n, evaled) in evaluated_args.into_iter().enumerate() {
let n = n as u8; let n = n as u8;
let mem = n.into(); let mem = n.into();
frame_state.environments.insert(mem, MemoryValue::Primitive(evaled)); frame_state.environments.insert(mem, MemoryValue::Primitive(evaled));
} }
frame_state.block(body) frame_state.block(body)
} }
} }

View File

@ -1,20 +1,19 @@
#![cfg(test)] #![cfg(test)]
use test_case::test_case; use test_case::test_case;
use crate::symbol_table::SymbolTable; use crate::{symbol_table::SymbolTable, tree_walk_eval::State};
use crate::tree_walk_eval::State;
fn evaluate_input(input: &str) -> Result<String, String> { fn evaluate_input(input: &str) -> Result<String, String> {
let ast = crate::util::quick_ast(input); let ast = crate::util::quick_ast(input);
let mut symbol_table = SymbolTable::new(); let mut symbol_table = SymbolTable::new();
symbol_table.process_ast(&ast).unwrap(); symbol_table.process_ast(&ast).unwrap();
let reduced_ir = crate::reduced_ir::reduce(&ast, &symbol_table); let reduced_ir = crate::reduced_ir::reduce(&ast, &symbol_table);
reduced_ir.debug(&symbol_table); reduced_ir.debug(&symbol_table);
println!("========"); println!("========");
symbol_table.debug(); symbol_table.debug();
let mut state = State::new(); let mut state = State::new();
let mut outputs = state.evaluate(reduced_ir, true); let mut outputs = state.evaluate(reduced_ir, true);
outputs.pop().unwrap() outputs.pop().unwrap()
} }
fn eval_assert(input: &str, expected: &str) { fn eval_assert(input: &str, expected: &str) {
@ -23,26 +22,26 @@ fn eval_assert(input: &str, expected: &str) {
#[test] #[test]
fn test_basic_eval() { fn test_basic_eval() {
eval_assert("1 + 2", "3"); eval_assert("1 + 2", "3");
eval_assert("let mut a = 1; a = 2", "()"); eval_assert("let mut a = 1; a = 2", "()");
eval_assert("let mut a = 1; a = a + 2; a", "3"); eval_assert("let mut a = 1; a = a + 2; a", "3");
} }
#[test] #[test]
fn op_eval() { fn op_eval() {
eval_assert("- 13", "-13"); eval_assert("- 13", "-13");
eval_assert("10 - 2", "8"); eval_assert("10 - 2", "8");
} }
#[test] #[test]
fn function_eval() { fn function_eval() {
eval_assert("fn oi(x) { x + 1 }; oi(4)", "5"); eval_assert("fn oi(x) { x + 1 }; oi(4)", "5");
eval_assert("fn oi(x) { x + 1 }; oi(1+2)", "4"); eval_assert("fn oi(x) { x + 1 }; oi(1+2)", "4");
} }
#[test] #[test]
fn scopes() { fn scopes() {
let scope_ok = r#" let scope_ok = r#"
let a = 20 let a = 20
fn haha() { fn haha() {
let something = 38 let something = 38
@ -79,7 +78,7 @@ let b = Option::Some(10)
#[test] #[test]
fn basic_if_statement() { fn basic_if_statement() {
let source = r#" let source = r#"
let a = 10 let a = 10
let b = 10 let b = 10
if a == b then { 69 } else { 420 } if a == b then { 69 } else { 420 }
@ -89,7 +88,7 @@ fn basic_if_statement() {
#[test] #[test]
fn basic_patterns_1() { fn basic_patterns_1() {
let source =r#" let source = r#"
let x = 10 let x = 10
let a = if x is 10 then { 255 } else { 256 } let a = if x is 10 then { 255 } else { 256 }
let b = if 23 is 99 then { 255 } else { 256 } let b = if 23 is 99 then { 255 } else { 256 }
@ -106,14 +105,16 @@ let d = if "xxx" is "yyy" then { 20 } else { 30 }
#[test_case("cyrus", "4")] #[test_case("cyrus", "4")]
fn basic_patterns_2(input: &str, expected: &str) { fn basic_patterns_2(input: &str, expected: &str) {
let mut source = format!(r#"let x = "{}""#, input); let mut source = format!(r#"let x = "{}""#, input);
source.push_str(r#" source.push_str(
r#"
if x { if x {
is "sanchez" then 1 is "sanchez" then 1
is "mouri" then 2 is "mouri" then 2
is "hella" then 3 is "hella" then 3
is _ then 4 is _ then 4
} }
"#); "#,
);
eval_assert(&source, expected); eval_assert(&source, expected);
} }
@ -121,31 +122,33 @@ if x {
#[test_case(r#"(99, "panda", false, -2.45)"#, r#""maybe""#)] #[test_case(r#"(99, "panda", false, -2.45)"#, r#""maybe""#)]
fn tuple_patterns(input: &str, expected: &str) { fn tuple_patterns(input: &str, expected: &str) {
let mut source = format!("let x = {}", input); let mut source = format!("let x = {}", input);
source.push_str(r#" source.push_str(
r#"
if x { if x {
is (45, "pablo", _, 28.4) then "no" is (45, "pablo", _, 28.4) then "no"
is (_, "panda", _, 2.2) then "yes" is (_, "panda", _, 2.2) then "yes"
is _ then "maybe" is _ then "maybe"
}"#); }"#,
);
eval_assert(&source, expected); eval_assert(&source, expected);
} }
#[test] #[test]
fn if_is_patterns() { fn if_is_patterns() {
let source = r#" let source = r#"
type Option<T> = Some(T) | None type Option<T> = Some(T) | None
let q = "a string" let q = "a string"
let x = Option::Some(9); if x is Option::Some(q) then { q } else { 0 }"#; let x = Option::Some(9); if x is Option::Some(q) then { q } else { 0 }"#;
eval_assert(source, "9"); eval_assert(source, "9");
let source = r#" let source = r#"
type Option<T> = Some(T) | None type Option<T> = Some(T) | None
let q = "a string" let q = "a string"
let outer = 2 let outer = 2
let x = Option::None; if x is Option::Some(q) then { q } else { -2 + outer }"#; let x = Option::None; if x is Option::Some(q) then { q } else { -2 + outer }"#;
eval_assert(source, "0"); eval_assert(source, "0");
} }
#[test] #[test]
@ -189,7 +192,7 @@ if a { is "foo" then "x", is _ then "y" }
#[test] #[test]
fn boolean_pattern() { fn boolean_pattern() {
let source = r#" let source = r#"
let a = true let a = true
if a { if a {
is true then "x", is true then "x",
@ -201,7 +204,7 @@ if a {
#[test] #[test]
fn boolean_pattern_2() { fn boolean_pattern_2() {
let source = r#" let source = r#"
let a = false let a = false
if a { is true then "x", is false then "y" } if a { is true then "x", is false then "y" }
"#; "#;
@ -210,7 +213,7 @@ if a { is true then "x", is false then "y" }
#[test] #[test]
fn ignore_pattern() { fn ignore_pattern() {
let source = r#" let source = r#"
type Option<T> = Some(T) | None type Option<T> = Some(T) | None
if Option::Some(10) { if Option::Some(10) {
is _ then "hella" is _ then "hella"
@ -221,7 +224,7 @@ if Option::Some(10) {
#[test] #[test]
fn tuple_pattern() { fn tuple_pattern() {
let source = r#" let source = r#"
if (1, 2) { if (1, 2) {
is (1, x) then x, is (1, x) then x,
is _ then 99 is _ then 99
@ -230,10 +233,9 @@ if (1, 2) {
eval_assert(source, "2"); eval_assert(source, "2");
} }
#[test] #[test]
fn tuple_pattern_2() { fn tuple_pattern_2() {
let source = r#" let source = r#"
if (1, 2) { if (1, 2) {
is (10, x) then x, is (10, x) then x,
is (y, x) then x + y is (y, x) then x + y
@ -244,7 +246,7 @@ if (1, 2) {
#[test] #[test]
fn tuple_pattern_3() { fn tuple_pattern_3() {
let source = r#" let source = r#"
if (1, 5) { if (1, 5) {
is (10, x) then x, is (10, x) then x,
is (1, x) then x is (1, x) then x
@ -255,7 +257,7 @@ if (1, 5) {
#[test] #[test]
fn tuple_pattern_4() { fn tuple_pattern_4() {
let source = r#" let source = r#"
if (1, 5) { if (1, 5) {
is (10, x) then x, is (10, x) then x,
is (1, x) then x, is (1, x) then x,
@ -264,10 +266,9 @@ if (1, 5) {
eval_assert(source, "5"); eval_assert(source, "5");
} }
#[test] #[test]
fn prim_obj_pattern() { fn prim_obj_pattern() {
let source = r#" let source = r#"
type Stuff = Mulch(Nat) | Jugs(Nat, String) | Mardok type Stuff = Mulch(Nat) | Jugs(Nat, String) | Mardok
let a = Stuff::Mulch(20) let a = Stuff::Mulch(20)
let b = Stuff::Jugs(1, "haha") let b = Stuff::Jugs(1, "haha")
@ -305,26 +306,26 @@ let y = \(m, n, o) { m + n + o }(1,2,3)
(x, y) (x, y)
"#; "#;
eval_assert(source, r"(10, 6)"); eval_assert(source, r"(10, 6)");
} }
#[test] #[test]
fn basic_lambda_evaluation_2() { fn basic_lambda_evaluation_2() {
let source = r#" let source = r#"
fn milta() { fn milta() {
\(x) { x + 33 } \(x) { x + 33 }
} }
milta()(10) milta()(10)
"#; "#;
eval_assert(source, "43"); eval_assert(source, "43");
} }
#[test] #[test]
fn import_all() { fn import_all() {
let source = r#" let source = r#"
type Option<T> = Some(T) | None type Option<T> = Some(T) | None
import Option::* import Option::*
let x = Some(9); if x is Some(q) then { q } else { 0 }"#; let x = Some(9); if x is Some(q) then { q } else { 0 }"#;
eval_assert(source, "9"); eval_assert(source, "9");
} }