Various data layout changes to support DataConstructor evaluation

This commit is contained in:
Greg Shuflin 2021-10-25 19:08:03 -07:00
parent a1d6661a6b
commit e4af5beb1c
5 changed files with 68 additions and 29 deletions

View File

@ -134,7 +134,7 @@ impl<'a> Reducer<'a> {
TupleLiteral(exprs) => Expression::Tuple(exprs.iter().map(|e| self.expression(e)).collect()),
IfExpression { discriminator, body, } => self.reduce_if_expression(discriminator.as_ref().map(|x| x.as_ref()), body),
Lambda { params, body, .. } => {
Expression::Callable(Function::Lambda {
Expression::Callable(Callable::Lambda {
arity: params.len() as u8,
body: self.function(body),
})
@ -169,7 +169,7 @@ impl<'a> Reducer<'a> {
match builtin {
Some(op) => {
Expression::Call {
f: Box::new(Expression::Callable(Function::Builtin(op))),
f: Box::new(Expression::Callable(Callable::Builtin(op))),
args: vec![self.expression(arg)],
}
}
@ -181,7 +181,8 @@ impl<'a> Reducer<'a> {
}
fn binop(&mut self, binop: &ast::BinOp, lhs: &ast::Expression, rhs: &ast::Expression) -> Expression {
use Expression::*;
use Expression::ReductionError;
let operation = Builtin::from_str(binop.sigil()).ok();
match operation {
Some(Builtin::Assignment) => {
@ -196,13 +197,13 @@ impl<'a> Reducer<'a> {
_ => return ReductionError("Trying to assign to a non-name".to_string()),
};
Assign {
Expression::Assign {
lval,
rval: Box::new(self.expression(rhs)),
}
},
Some(op) => Expression::Call {
f: Box::new(Expression::Callable(Function::Builtin(op))),
f: Box::new(Expression::Callable(Callable::Builtin(op))),
args: vec![self.expression(lhs), self.expression(rhs)],
},
//TODO handle a user-defined operation
@ -225,9 +226,11 @@ impl<'a> Reducer<'a> {
GlobalBinding => Expression::Lookup(Lookup::GlobalVar(def_id.unwrap())),
LocalVariable => Expression::Lookup(Lookup::LocalVar(def_id.unwrap())),
FunctionParam(n) => Expression::Lookup(Lookup::Param(n)),
DataConstructor { index, arity, type_id } => {
Expression::ReductionError("DataConstructor not supported".to_string())
},
DataConstructor { index, arity, type_id } => Expression::Callable(Callable::DataConstructor {
type_id: type_id.clone(),
arity: arity as u32, //TODO fix up these modifiers
tag: index as u32,
}),
RecordConstructor { .. } => {
Expression::ReductionError(format!("The symbol for value {:?} is unexpectdly a RecordConstructor", qualified_name))
},

View File

@ -3,6 +3,7 @@ use std::rc::Rc;
use crate::builtin::Builtin;
use crate::symbol_table::{DefId, SymbolTable};
use crate::typechecking::TypeId;
//TODO most of these Clone impls only exist to support function application, because the
//tree-walking evaluator moves the reduced IR members.
@ -55,7 +56,7 @@ pub enum Expression {
lval: DefId,
rval: Box<Expression>,
},
Callable(Function),
Callable(Callable),
Call {
f: Box<Expression>,
args: Vec<Expression>
@ -75,7 +76,7 @@ pub struct FunctionDefinition {
}
#[derive(Debug, Clone)]
pub enum Function {
pub enum Callable {
Builtin(Builtin),
UserDefined(DefId),
Lambda {
@ -83,7 +84,7 @@ pub enum Function {
body: Vec<Statement>
},
DataConstructor {
type_id: Rc<String>, //TODO this can't last
type_id: TypeId,
arity: u32,
tag: u32
},

View File

@ -1,7 +1,8 @@
use crate::reduced_ir::{ReducedIR, Expression, Lookup, Function, FunctionDefinition, Statement, Literal};
use crate::reduced_ir::{ReducedIR, Expression, Lookup, Callable, FunctionDefinition, Statement, Literal};
use crate::symbol_table::{DefId};
use crate::util::ScopeStack;
use crate::builtin::Builtin;
use crate::typechecking::TypeId;
use std::fmt::Write;
use std::rc::Rc;
@ -106,10 +107,21 @@ fn expr_to_repl(expr: &Expression) -> String {
}
}
impl Primitive {
fn to_repl(&self) -> String {
match self {
Primitive::Object { type_id, items, .. } => {
format!("{}{}", type_id.local_name(), paren_wrapped(items.iter().map(|item| item.to_repl())))
},
prim => expr_to_repl(&prim.to_expr()),
}
}
}
impl RuntimeValue {
fn to_repl(&self) -> String {
match self {
RuntimeValue::Primitive(ref prim) => expr_to_repl(&prim.to_expr()),
RuntimeValue::Primitive(ref prim) => prim.to_repl(),
RuntimeValue::Function(..) => "<function>".to_string(),
}
}
@ -120,14 +132,12 @@ impl RuntimeValue {
enum Primitive {
Tuple(Vec<Primitive>),
Literal(Literal),
Callable(Function),
/*
PrimObject {
name: Rc<String>,
tag: usize,
items: Vec<Node>,
Callable(Callable),
Object {
type_id: TypeId,
tag: u32,
items: Vec<Primitive>
},
*/
}
impl Primitive {
@ -148,6 +158,13 @@ impl Primitive {
Primitive::Tuple(items) => Expression::Tuple(items.iter().map(|item| item.to_expr()).collect()),
Primitive::Literal(lit) => Expression::Literal(lit.clone()),
Primitive::Callable(function) => Expression::Callable(function.clone()),
Primitive::Object { type_id, tag, items } if items.len() == 0 => {
Expression::Literal(Literal::Nat(420))
},
Primitive::Object { type_id, tag, items } => Expression::Call {
f: Box::new(Expression::Callable(Callable::DataConstructor { type_id: type_id.clone(), arity: items.len() as u32, tag: *tag as u32 })),
args: items.iter().map(|arg| arg.to_expr()).collect(),
},
}
}
}
@ -223,7 +240,7 @@ impl<'a> State<'a> {
match self.environments.lookup(&mem) {
// This just checks that the function exists in "memory" by ID, we don't
// actually retrieve it until `apply_function()`
Some(RuntimeValue::Function(_)) => Primitive::Callable(Function::UserDefined(id.clone())),
Some(RuntimeValue::Function(_)) => Primitive::Callable(Callable::UserDefined(id.clone())),
x => return Err(format!("Function not found for id: {} : {:?}", id, x).into()),
}
},
@ -260,8 +277,8 @@ impl<'a> State<'a> {
other => return Err(format!("Trying to call non-function value: {:?}", other).into()),
};
match func {
Function::Builtin(builtin) => self.apply_builtin(builtin, args),
Function::UserDefined(def_id) => {
Callable::Builtin(builtin) => self.apply_builtin(builtin, args),
Callable::UserDefined(def_id) => {
let mem = (&def_id).into();
match self.environments.lookup(&mem) {
Some(RuntimeValue::Function(FunctionDefinition { body })) => {
@ -271,14 +288,28 @@ impl<'a> State<'a> {
e => Err(format!("Error looking up function with id {}: {:?}", def_id, e).into())
}
},
Function::Lambda { arity, body } => {
Callable::Lambda { arity, body } => {
if arity as usize != args.len() {
return Err(format!("Lambda expression requries {} arguments, only {} provided", arity, args.len()).into());
}
let body = body.clone(); //TODO again ideally, no cloning here
self.apply_function(body, args)
}
Function::DataConstructor { .. } => panic!(),
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![];
for arg in args.into_iter() {
evaluated_args.push(self.expression(arg)?);
}
Ok(Primitive::Object {
type_id,
tag,
items: evaluated_args
})
}
}
}
@ -365,7 +396,6 @@ impl<'a> State<'a> {
}
fn apply_function(&mut self, body: Vec<Statement>, args: Vec<Expression>) -> EvalResult<Primitive> {
let mut evaluated_args: Vec<Primitive> = vec![];
for arg in args.into_iter() {
evaluated_args.push(self.expression(arg)?);

View File

@ -68,10 +68,11 @@ fn adt_output_1() {
let source = r#"
type Option<T> = Some(T) | None
let x = Option::Some(10)
x
let a = Option::None
let b = Option::Some(10)
(a, b)
"#;
eval_assert(source, "Option::Some(10)");
eval_assert(source, "(Option::None, Option::Some(10))");
}
/*

View File

@ -35,6 +35,10 @@ impl TypeId {
local_name: Rc::new(name.to_string())
}
}
pub fn local_name(&self) -> &str {
self.local_name.as_ref()
}
}
impl fmt::Display for TypeId {