Compare commits
2 Commits
0c6d2be95a
...
d65233240a
Author | SHA1 | Date | |
---|---|---|---|
|
d65233240a | ||
|
a3463f5519 |
8
rustfmt.toml
Normal file
8
rustfmt.toml
Normal 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
|
@ -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)]
|
||||||
|
74
schala-lang/language/src/identifier.rs
Normal file
74
schala-lang/language/src/identifier.rs
Normal 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;
|
@ -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;
|
||||||
|
|
||||||
|
@ -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(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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(),
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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");
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user