From 292fdb13051639a4574339b5c01b72df94406f4a Mon Sep 17 00:00:00 2001 From: James Harton Date: Thu, 14 Mar 2019 08:29:01 +1300 Subject: [PATCH] Add type, trait and impl definitions. --- huia-compiler/src/ir/builder.rs | 133 ++++++++++++++++++++++++-------- huia-compiler/src/ir/mod.rs | 11 +++ huia-compiler/src/ty.rs | 5 ++ 3 files changed, 118 insertions(+), 31 deletions(-) diff --git a/huia-compiler/src/ir/builder.rs b/huia-compiler/src/ir/builder.rs index ef6baf8..fb229ec 100644 --- a/huia-compiler/src/ir/builder.rs +++ b/huia-compiler/src/ir/builder.rs @@ -1,12 +1,19 @@ use crate::context::Context; use crate::ir::{Val, IR}; +use crate::ty::TyIdx; use huia_parser::ast::{Location, NodeType, Term, Value}; /// The Builder is a simple stack machine for converting AST into IR. -#[derive(Default)] +/// FIXME: +/// Maybe push a named scope somehow when defining types so that we can figure +/// out where we are when building methods, etc. +#[derive(Default, Debug)] struct Builder { /// The IR stack - used to compose intermediate values. - stack: Vec, + ir_stack: Vec, + + /// Type stack - used to track which type is currently being defined. + ty_stack: Vec, } impl Builder { @@ -20,41 +27,41 @@ impl Builder { let location = context.location(&node.location()); let ty = context.reference_type(&ty_idx, location); let idx = context.constant_string(node.atom().unwrap().value_ref().as_str()); - self.stack.push(IR::Constant(ty, Val::Atom(idx))); + self.push_ir(IR::Constant(ty, Val::Atom(idx))); } NodeType::Boolean => { let ty_idx = context.constant_string("Huia.Native.Boolean"); let location = context.location(&node.location()); let ty = context.reference_type(&ty_idx, location); let value = *node.boolean().unwrap().value_ref(); - self.stack.push(IR::Constant(ty, Val::Boolean(value))); + self.push_ir(IR::Constant(ty, Val::Boolean(value))); } NodeType::Float => { let ty_idx = context.constant_string("Huia.Native.Float"); let location = context.location(&node.location()); let ty = context.reference_type(&ty_idx, location); let value = *node.float().unwrap().value_ref(); - self.stack.push(IR::Constant(ty, Val::Float(value))); + self.push_ir(IR::Constant(ty, Val::Float(value))); } NodeType::Integer => { let ty_idx = context.constant_string("Huia.Native.Integer"); let location = context.location(&node.location()); let ty = context.reference_type(&ty_idx, location); let value = *node.integer().unwrap().value_ref(); - self.stack.push(IR::Constant(ty, Val::Integer(value))); + self.push_ir(IR::Constant(ty, Val::Integer(value))); } NodeType::String => { let ty_idx = context.constant_string("Huia.Native.String"); let location = context.location(&node.location()); let ty = context.reference_type(&ty_idx, location); let idx = context.constant_string(node.string().unwrap().value_ref().as_str()); - self.stack.push(IR::Constant(ty, Val::String(idx))); + self.push_ir(IR::Constant(ty, Val::String(idx))); } NodeType::Ty => { let ty_idx = context.constant_string(node.ty().unwrap().value_ref().as_str()); let location = context.location(&node.location()); let ty = context.reference_type(&ty_idx, location); - self.stack.push(IR::TypeReference(ty)); + self.push_ir(IR::TypeReference(ty)); } NodeType::Array => { let ty_idx = context.constant_string("Huia.Native.Array"); @@ -67,10 +74,10 @@ impl Builder { .iter() .map(|node| { self.build(node.clone(), &mut context); - self.pop().unwrap() + self.pop_ir().unwrap() }) .collect(); - self.stack.push(IR::Constant(ty, Val::Array(elements))); + self.push_ir(IR::Constant(ty, Val::Array(elements))); } NodeType::Map => { let ty_idx = context.constant_string("Huia.Native.Map"); @@ -83,22 +90,21 @@ impl Builder { .iter() .map(|(key, value)| { self.build(key.clone(), &mut context); - let key = self.pop().unwrap(); + let key = self.pop_ir().unwrap(); self.build(value.clone(), &mut context); - let value = self.pop().unwrap(); + let value = self.pop_ir().unwrap(); (key, value) }) .collect(); - self.stack.push(IR::Constant(ty, Val::Map(elements))); + self.push_ir(IR::Constant(ty, Val::Map(elements))); } NodeType::Binary => { let (op, lhs, rhs) = node.binary().unwrap(); self.build(lhs.clone(), &mut context); self.build(rhs.clone(), &mut context); - let rhs = self.pop().unwrap(); - let lhs = self.pop().unwrap(); - self.stack - .push(IR::Infix(op.value_ref().clone(), Box::new((lhs, rhs)))); + let rhs = self.pop_ir().unwrap(); + let lhs = self.pop_ir().unwrap(); + self.push_ir(IR::Infix(op.value_ref().clone(), Box::new((lhs, rhs)))); } NodeType::Constructor => { let (ty, props) = node.constructor().unwrap(); @@ -111,18 +117,17 @@ impl Builder { .map(|(key, value)| { let key = context.constant_string(key.value_ref().as_str()); self.build(value.clone(), &mut context); - let value = self.stack.pop().unwrap(); + let value = self.ir_stack.pop().unwrap(); (key, value) }) .collect(); - self.stack.push(IR::Constructor(ty, elements)); + self.push_ir(IR::Constructor(ty, elements)); } NodeType::Unary => { let (op, rhs) = node.unary().unwrap(); self.build(rhs.clone(), &mut context); - let rhs = self.pop().unwrap(); - self.stack - .push(IR::Unary(op.value_ref().clone(), Box::new(rhs))); + let rhs = self.pop_ir().unwrap(); + self.push_ir(IR::Unary(op.value_ref().clone(), Box::new(rhs))); } NodeType::Call => { let (name, args) = node.call().unwrap(); @@ -132,18 +137,18 @@ impl Builder { .iter() .map(|node| { self.build(node.clone(), &mut context); - self.pop().unwrap() + self.pop_ir().unwrap() }) .collect(); - self.stack.push(IR::Call(name, elements)); + self.push_ir(IR::Call(name, elements)); } NodeType::Declaration => { let (name, node) = node.declaration().unwrap(); let name = context.constant_string(name.value_ref().as_str()); self.build(node.clone(), &mut context); - let node = self.pop().unwrap(); - self.stack.push(IR::Declaration(name, Box::new(node))); + let node = self.pop_ir().unwrap(); + self.push_ir(IR::Declaration(name, Box::new(node))); } NodeType::TypeDef => { let (ty, properties, body) = node.typedef().unwrap(); @@ -169,12 +174,15 @@ impl Builder { }) .collect(); + let ty = context.declare_type(&ty_idx, properties, location); + self.push_ty(ty.clone()); + for node in body { self.build(node.clone(), &mut context); } - let ty = context.declare_type(&ty_idx, properties, location); - self.stack.push(IR::TypeDefinition(ty)); + self.pop_ty(); + self.push_ir(IR::TypeDefinition(ty)); } NodeType::TraitDef => { let (ty, typespec, body) = node.traitdef().unwrap(); @@ -196,19 +204,59 @@ impl Builder { Vec::new() }; + let ty = context.declare_trait(&ty_idx, types, location); + self.push_ty(ty.clone()); + for node in body { self.build(node.clone(), &mut context); } - let ty = context.declare_trait(&ty_idx, types, location); - self.stack.push(IR::TraitDefinition(ty)); + self.pop_ty(); + self.push_ir(IR::TraitDefinition(ty)); + } + NodeType::ImplDef => { + let (ty, body) = node.impldef().unwrap(); + let ty_idx = context.constant_string(ty.value_ref().as_str()); + let location = context.location(&node.location()); + let ty = context.reference_type(&ty_idx, location); + + // the parser state should stop us from being in an + // implementation whilst not inside a type definition. + let current_type = self.peek_ty().unwrap().clone(); + + for node in body { + self.build(node.clone(), &mut context); + } + + self.push_ir(IR::ImplementTrait(current_type, ty)); } _ => (), } } pub fn pop(&mut self) -> Option { - self.stack.pop() + self.pop_ir() + } + + fn push_ir(&mut self, ir: IR) { + self.ir_stack.push(ir); + } + + fn pop_ir(&mut self) -> Option { + self.ir_stack.pop() + } + + fn push_ty(&mut self, ty: TyIdx) { + self.ty_stack.push(ty); + } + + fn pop_ty(&mut self) -> Option { + self.ty_stack.pop() + } + + fn peek_ty(&self) -> Option<&TyIdx> { + let len = self.ty_stack.len(); + self.ty_stack.get(len - 1) } } @@ -356,4 +404,27 @@ mod test { assert!(ir.is_trait_definition()); } + #[test] + fn test_trait_implementation() { + let term = Term::file( + r#" + type Delorean(speed: Integer) do + impl TimeMachine + end + "#, + ) + .unwrap()[0] + .clone(); + let mut builder = Builder::default(); + let mut context = Context::test(); + builder.build(term, &mut context); + + // The type definition is pushed onto the stack last. + let ir = builder.pop().unwrap(); + assert!(ir.is_type_definition()); + + let ir = builder.pop().unwrap(); + assert!(ir.is_trait_implementation()); + } + } diff --git a/huia-compiler/src/ir/mod.rs b/huia-compiler/src/ir/mod.rs index b0134d6..a8702c6 100644 --- a/huia-compiler/src/ir/mod.rs +++ b/huia-compiler/src/ir/mod.rs @@ -6,6 +6,9 @@ use huia_parser::ast::binary::Operator as BinOp; use huia_parser::ast::unary::Operator as UnOp; /// Describes the intermediate representation as converted from the Parser's AST. +/// +/// FIXME: +/// IR should have a location too if possible. #[derive(Debug)] pub enum IR { Constant(TyIdx, Val), @@ -17,6 +20,7 @@ pub enum IR { Declaration(StringIdx, Box), TypeDefinition(TyIdx), TraitDefinition(TyIdx), + ImplementTrait(TyIdx, TyIdx), } impl IR { @@ -82,6 +86,13 @@ impl IR { _ => false, } } + + pub fn is_trait_implementation(&self) -> bool { + match self { + IR::ImplementTrait(..) => true, + _ => false, + } + } } /// A constant value. diff --git a/huia-compiler/src/ty.rs b/huia-compiler/src/ty.rs index 99dc05e..bd18485 100644 --- a/huia-compiler/src/ty.rs +++ b/huia-compiler/src/ty.rs @@ -2,6 +2,11 @@ use crate::location::Location; use crate::stable::StringIdx; use std::fmt; +/// The main "Type" struct. +/// Holds information about Huia Types and Traits. +/// +/// FIXME: +/// Should probably support the Native option/result types. #[derive(Debug, PartialEq)] pub struct Ty { name: Option,