diff --git a/huia-compiler/src/ir/builder.rs b/huia-compiler/src/ir/builder.rs index 95295e0..cb44e56 100644 --- a/huia-compiler/src/ir/builder.rs +++ b/huia-compiler/src/ir/builder.rs @@ -23,7 +23,7 @@ impl Builder { NodeType::Array => { let ty_idx = context.constant_string("Huia.Native.Array"); let location = context.location(&node.location()); - let ty = context.reference_type(&ty_idx, location); + let ty = context.reference_type(&ty_idx, location.clone()); let elements = node .array() @@ -34,32 +34,41 @@ impl Builder { self.pop_ir().unwrap() }) .collect(); - self.push_ir(IR::Constant(ty, Val::Array(elements))); + self.push_ir(IR::new_constant(ty, location, Val::Array(elements))); } NodeType::Atom => { let ty_idx = context.constant_string("Huia.Native.Atom"); let location = context.location(&node.location()); - let ty = context.reference_type(&ty_idx, location); + let ty = context.reference_type(&ty_idx, location.clone()); let idx = context.constant_string(node.atom().unwrap().value_ref().as_str()); - self.push_ir(IR::Constant(ty, Val::Atom(idx))); + self.push_ir(IR::new_constant(ty, location, 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 ty = context.reference_type(&ty_idx, location.clone()); let value = *node.boolean().unwrap().value_ref(); - self.push_ir(IR::Constant(ty, Val::Boolean(value))); + self.push_ir(IR::new_constant(ty, location, Val::Boolean(value))); } NodeType::Binary => { + let location = context.location(&node.location()); let (op, lhs, rhs) = node.binary().unwrap(); self.build(lhs.clone(), &mut context); self.build(rhs.clone(), &mut context); 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)))); + let result_type = lhs.result_type(); + self.push_ir(IR::new_infix( + result_type, + location, + op.value_ref().clone(), + lhs, + rhs, + )); } NodeType::Call => { let (name, args) = node.call().unwrap(); + let location = context.location(&node.location()); let name = context.constant_string(name.value_ref().as_str()); self.push_block(); @@ -68,16 +77,22 @@ impl Builder { } let arguments = self.pop_block().unwrap().ir(); - self.push_ir(IR::GetLocal(name)); - self.push_ir(IR::Call(arguments)); + let result_type = context.unknown_type(location.clone()); + + self.push_ir(IR::new_get_local( + result_type.clone(), + location.clone(), + name, + )); + self.push_ir(IR::new_call(result_type, location, arguments)); } NodeType::Constructor => { let (ty, props) = node.constructor().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); + let ty = context.reference_type(&ty_idx, location.clone()); - let elements = props + let properties = props .iter() .map(|(key, value)| { let key = context.constant_string(key.value_ref().as_str()); @@ -86,27 +101,29 @@ impl Builder { (key, value) }) .collect(); - self.push_ir(IR::Constructor(ty, elements)); + self.push_ir(IR::new_constructor(ty, location, properties)); } NodeType::Declaration => { + let location = context.location(&node.location()); let (name, rhs) = node.declaration().unwrap(); let name = context.constant_string(name.value_ref().as_str()); self.build(rhs.clone(), &mut context); let rhs = self.pop_ir().unwrap(); - self.push_ir(IR::SetLocal(name, Box::new(rhs))); + let result_type = context.unknown_type(location.clone()); + self.push_ir(IR::new_set_local(result_type, location, name, rhs)); } 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 ty = context.reference_type(&ty_idx, location.clone()); let value = *node.float().unwrap().value_ref(); - self.push_ir(IR::Constant(ty, Val::Float(value))); + self.push_ir(IR::new_constant(ty, location, Val::Float(value))); } NodeType::Function => { let function = node.function().unwrap(); let location = context.location(&node.location()); - let return_type = context.unknown_type(location); - let mut fun = Function::new(return_type); + let return_type = context.unknown_type(location.clone()); + let mut fun = Function::new(return_type.clone()); for clause in function.value_ref() { let arguments: Vec<(StringIdx, TyIdx)> = clause @@ -141,7 +158,7 @@ impl Builder { } let fun_idx = context.define_function(fun); - self.push_ir(IR::Function(fun_idx)); + self.push_ir(IR::new_function_ref(return_type, location, fun_idx)); } NodeType::If => { let (test, positive, negative) = node.if_expr().unwrap(); @@ -149,7 +166,7 @@ impl Builder { self.build(test.clone(), &mut context); let test = self.pop_ir().unwrap(); - let result_type = context.unknown_type(location); + let result_type = context.unknown_type(location.clone()); self.push_block(); for node in positive { @@ -165,11 +182,12 @@ impl Builder { let negative = self.pop_block().unwrap(); let negative_idx = context.push_block(negative); - self.push_ir(IR::If( - Box::new(test), + self.push_ir(IR::new_if( + result_type, + location, + test, positive_idx, negative_idx, - result_type, )); } NodeType::ImplDef => { @@ -198,19 +216,21 @@ impl Builder { 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 ty = context.reference_type(&ty_idx, location.clone()); let value = *node.integer().unwrap().value_ref(); - self.push_ir(IR::Constant(ty, Val::Integer(value))); + self.push_ir(IR::new_constant(ty, location, Val::Integer(value))); } NodeType::Local => { let name = node.local().unwrap(); + let location = context.location(&node.location()); + let result_type = context.unknown_type(location.clone()); let name = context.constant_string(name.value_ref().as_str()); - self.push_ir(IR::GetLocal(name)); + self.push_ir(IR::new_get_local(result_type, location, name)); } NodeType::Map => { let ty_idx = context.constant_string("Huia.Native.Map"); let location = context.location(&node.location()); - let ty = context.reference_type(&ty_idx, location); + let ty = context.reference_type(&ty_idx, location.clone()); let elements = node .map() @@ -224,14 +244,18 @@ impl Builder { (key, value) }) .collect(); - self.push_ir(IR::Constant(ty, Val::Map(elements))); + self.push_ir(IR::new_constant(ty, location, Val::Map(elements))); } NodeType::PropertyGet => { + let location = context.location(&node.location()); + let result_type = context.unknown_type(location.clone()); let ident = context.constant_string(node.property_get().unwrap().value_ref().as_str()); - self.push_ir(IR::GetProperty(ident)); + self.push_ir(IR::new_get_property(result_type, location, ident)); } NodeType::PropertySet => { + let location = context.location(&node.location()); + let result_type = context.unknown_type(location.clone()); let elements: Vec<(StringIdx, IR)> = node .property_set() .unwrap() @@ -245,7 +269,7 @@ impl Builder { }) .collect(); - self.push_ir(IR::SetProperties(elements)) + self.push_ir(IR::new_set_properties(result_type, location, elements)) } NodeType::PrivateMethod => { let current_ty = self.peek_ty().unwrap(); @@ -412,9 +436,9 @@ impl Builder { 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 ty = context.reference_type(&ty_idx, location.clone()); let idx = context.constant_string(node.string().unwrap().value_ref().as_str()); - self.push_ir(IR::Constant(ty, Val::String(idx))); + self.push_ir(IR::new_constant(ty, location, Val::String(idx))); } NodeType::TraitDef => { let (ty, typespec, body) = node.traitdef().unwrap(); @@ -448,10 +472,12 @@ impl Builder { self.pop_ty(); } 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.push_ir(IR::TypeReference(ty)); + let result_type_idx = context.constant_string("Huia.Native.Type"); + let result_type = context.reference_type(&result_type_idx, location.clone()); + let ty_idx = context.constant_string(node.ty().unwrap().value_ref().as_str()); + let ty = context.reference_type(&ty_idx, location.clone()); + self.push_ir(IR::new_type_reference(result_type, location, ty)); } NodeType::TypeDef => { let (ty, properties, body) = node.typedef().unwrap(); @@ -488,10 +514,17 @@ impl Builder { self.pop_ty(); } NodeType::Unary => { + let location = context.location(&node.location()); let (op, rhs) = node.unary().unwrap(); self.build(rhs.clone(), &mut context); let rhs = self.pop_ir().unwrap(); - self.push_ir(IR::Unary(op.value_ref().clone(), Box::new(rhs))); + let result_type = rhs.result_type(); + self.push_ir(IR::new_unary( + result_type, + location, + op.value_ref().clone(), + rhs, + )); } _ => unreachable!("Unexpected node type: {:?}", node.node_type()), diff --git a/huia-compiler/src/ir/mod.rs b/huia-compiler/src/ir/mod.rs index 82bc8b6..05f49f8 100644 --- a/huia-compiler/src/ir/mod.rs +++ b/huia-compiler/src/ir/mod.rs @@ -2,33 +2,233 @@ mod builder; use crate::block::BlockIdx; use crate::function::FunctionIdx; +use crate::location::Location; use crate::stable::StringIdx; use crate::ty::TyIdx; 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, PartialEq)] -pub enum IR { +pub struct IR { + location: Option, + result_type: TyIdx, + kind: IRKind, +} + +impl IR { + pub fn new_call(result_type: TyIdx, location: Location, arguments: Vec) -> IR { + IR { + location: Some(location), + result_type, + kind: IRKind::Call(arguments), + } + } + + pub fn new_constant(result_type: TyIdx, location: Location, value: Val) -> IR { + IR { + location: Some(location), + result_type, + kind: IRKind::Constant(value), + } + } + + pub fn new_constructor( + result_type: TyIdx, + location: Location, + properties: Vec<(StringIdx, IR)>, + ) -> IR { + IR { + location: Some(location), + result_type, + kind: IRKind::Constructor(properties), + } + } + + pub fn new_function_ref(result_type: TyIdx, location: Location, function: FunctionIdx) -> IR { + IR { + location: Some(location), + result_type, + kind: IRKind::FunctionRef(function), + } + } + + pub fn new_get_local(result_type: TyIdx, location: Location, name: StringIdx) -> IR { + IR { + location: Some(location), + result_type, + kind: IRKind::GetLocal(name), + } + } + + pub fn new_get_property(result_type: TyIdx, location: Location, name: StringIdx) -> IR { + IR { + location: Some(location), + result_type, + kind: IRKind::GetProperty(name), + } + } + + pub fn new_if( + result_type: TyIdx, + location: Location, + test: IR, + positive: BlockIdx, + negative: BlockIdx, + ) -> IR { + IR { + location: Some(location), + result_type, + kind: IRKind::If(Box::new(test), positive, negative), + } + } + + pub fn new_infix(result_type: TyIdx, location: Location, op: BinOp, lhs: IR, rhs: IR) -> IR { + let lhs = Box::new(lhs); + let rhs = Box::new(rhs); + IR { + location: Some(location), + result_type, + kind: IRKind::Infix(op, lhs, rhs), + } + } + + pub fn new_set_local(result_type: TyIdx, location: Location, name: StringIdx, value: IR) -> IR { + IR { + location: Some(location), + result_type, + kind: IRKind::SetLocal(name, Box::new(value)), + } + } + + pub fn new_set_properties( + result_type: TyIdx, + location: Location, + properties: Vec<(StringIdx, IR)>, + ) -> IR { + IR { + location: Some(location), + result_type, + kind: IRKind::SetProperties(properties), + } + } + + pub fn new_type_reference(result_type: TyIdx, location: Location, ty: TyIdx) -> IR { + IR { + location: Some(location), + result_type, + kind: IRKind::TypeReference(ty), + } + } + + pub fn new_unary(result_type: TyIdx, location: Location, op: UnOp, rhs: IR) -> IR { + IR { + location: Some(location), + result_type, + kind: IRKind::Unary(op, Box::new(rhs)), + } + } + + pub fn function(&self) -> Option<&FunctionIdx> { + match self.kind { + IRKind::FunctionRef(ref idx) => Some(idx), + _ => None, + } + } + + pub fn is_call(&self) -> bool { + match self.kind { + IRKind::Call(..) => true, + _ => false, + } + } + + pub fn is_constant(&self) -> bool { + match self.kind { + IRKind::Constant(..) => true, + _ => false, + } + } + + pub fn is_constuctor(&self) -> bool { + match self.kind { + IRKind::Constructor(..) => true, + _ => false, + } + } + + pub fn is_function(&self) -> bool { + match self.kind { + IRKind::FunctionRef(..) => true, + _ => false, + } + } + + pub fn is_get_local(&self) -> bool { + match self.kind { + IRKind::GetLocal(..) => true, + _ => false, + } + } + + pub fn is_get_property(&self) -> bool { + match self.kind { + IRKind::GetProperty(..) => true, + _ => false, + } + } + + pub fn is_infix(&self) -> bool { + match self.kind { + IRKind::Infix(..) => true, + _ => false, + } + } + + pub fn is_type_reference(&self) -> bool { + match self.kind { + IRKind::TypeReference(..) => true, + _ => false, + } + } + + pub fn is_set_local(&self) -> bool { + match self.kind { + IRKind::SetLocal(..) => true, + _ => false, + } + } + + pub fn is_unary(&self) -> bool { + match self.kind { + IRKind::Unary(..) => true, + _ => false, + } + } + + pub fn result_type(&self) -> TyIdx { + self.result_type.clone() + } +} + +#[derive(Debug, PartialEq)] +enum IRKind { /// Call top of the stack with arguments. Call(Vec), /// A constant value of the specified type. - Constant(TyIdx, Val), + Constant(Val), /// Construct an instance of type with the provided properties. - Constructor(TyIdx, Vec<(StringIdx, IR)>), + Constructor(Vec<(StringIdx, IR)>), /// Reference an anonymous function from the context. - Function(FunctionIdx), + FunctionRef(FunctionIdx), /// Reference a local variable. GetLocal(StringIdx), /// Refernce an instance property. GetProperty(StringIdx), - /// Consists of test, positives, negatives and a return type. - If(Box, BlockIdx, BlockIdx, TyIdx), + /// Consists of test, positives, negatives. + If(Box, BlockIdx, BlockIdx), /// A binary operation with LHS and RHS. - Infix(BinOp, Box<(IR, IR)>), + Infix(BinOp, Box, Box), /// Take a value and set it as a local variable. SetLocal(StringIdx, Box), /// Set properties on an instance, returning the modified instance. @@ -39,85 +239,6 @@ pub enum IR { Unary(UnOp, Box), } -impl IR { - pub fn function(&self) -> Option<&FunctionIdx> { - match self { - IR::Function(ref idx) => Some(idx), - _ => None, - } - } - - pub fn is_call(&self) -> bool { - match self { - IR::Call(..) => true, - _ => false, - } - } - - pub fn is_constant(&self) -> bool { - match self { - IR::Constant(..) => true, - _ => false, - } - } - - pub fn is_constuctor(&self) -> bool { - match self { - IR::Constructor(..) => true, - _ => false, - } - } - - pub fn is_function(&self) -> bool { - match self { - IR::Function(..) => true, - _ => false, - } - } - - pub fn is_get_local(&self) -> bool { - match self { - IR::GetLocal(..) => true, - _ => false, - } - } - - pub fn is_get_property(&self) -> bool { - match self { - IR::GetProperty(..) => true, - _ => false, - } - } - - pub fn is_infix(&self) -> bool { - match self { - IR::Infix(..) => true, - _ => false, - } - } - - pub fn is_type_reference(&self) -> bool { - match self { - IR::TypeReference(..) => true, - _ => false, - } - } - - pub fn is_set_local(&self) -> bool { - match self { - IR::SetLocal(..) => true, - _ => false, - } - } - - pub fn is_unary(&self) -> bool { - match self { - IR::Unary(..) => true, - _ => false, - } - } -} - /// A constant value. #[derive(Debug, PartialEq)] pub enum Val {