From e4af5beb1ced13a230af36deb9e1fc3a2d339f13 Mon Sep 17 00:00:00 2001 From: Greg Shuflin Date: Mon, 25 Oct 2021 19:08:03 -0700 Subject: [PATCH] Various data layout changes to support DataConstructor evaluation --- schala-lang/language/src/reduced_ir/mod.rs | 19 +++--- schala-lang/language/src/reduced_ir/types.rs | 7 ++- .../language/src/tree_walk_eval/mod.rs | 60 ++++++++++++++----- .../language/src/tree_walk_eval/test.rs | 7 ++- schala-lang/language/src/typechecking.rs | 4 ++ 5 files changed, 68 insertions(+), 29 deletions(-) diff --git a/schala-lang/language/src/reduced_ir/mod.rs b/schala-lang/language/src/reduced_ir/mod.rs index 67bd8ad..79a29ec 100644 --- a/schala-lang/language/src/reduced_ir/mod.rs +++ b/schala-lang/language/src/reduced_ir/mod.rs @@ -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)) }, diff --git a/schala-lang/language/src/reduced_ir/types.rs b/schala-lang/language/src/reduced_ir/types.rs index 37af6b6..cad89ff 100644 --- a/schala-lang/language/src/reduced_ir/types.rs +++ b/schala-lang/language/src/reduced_ir/types.rs @@ -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, }, - Callable(Function), + Callable(Callable), Call { f: Box, args: Vec @@ -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 }, DataConstructor { - type_id: Rc, //TODO this can't last + type_id: TypeId, arity: u32, tag: u32 }, diff --git a/schala-lang/language/src/tree_walk_eval/mod.rs b/schala-lang/language/src/tree_walk_eval/mod.rs index a868b39..da2e5e1 100644 --- a/schala-lang/language/src/tree_walk_eval/mod.rs +++ b/schala-lang/language/src/tree_walk_eval/mod.rs @@ -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(..) => "".to_string(), } } @@ -120,14 +132,12 @@ impl RuntimeValue { enum Primitive { Tuple(Vec), Literal(Literal), - Callable(Function), - /* - PrimObject { - name: Rc, - tag: usize, - items: Vec, + Callable(Callable), + Object { + type_id: TypeId, + tag: u32, + items: Vec }, - */ } 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 = 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, args: Vec) -> EvalResult { - let mut evaluated_args: Vec = vec![]; for arg in args.into_iter() { evaluated_args.push(self.expression(arg)?); diff --git a/schala-lang/language/src/tree_walk_eval/test.rs b/schala-lang/language/src/tree_walk_eval/test.rs index 828b491..9a67963 100644 --- a/schala-lang/language/src/tree_walk_eval/test.rs +++ b/schala-lang/language/src/tree_walk_eval/test.rs @@ -68,10 +68,11 @@ fn adt_output_1() { let source = r#" type Option = 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))"); } /* diff --git a/schala-lang/language/src/typechecking.rs b/schala-lang/language/src/typechecking.rs index 75780a8..8aa61f4 100644 --- a/schala-lang/language/src/typechecking.rs +++ b/schala-lang/language/src/typechecking.rs @@ -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 {