diff --git a/huia-compiler/src/context.rs b/huia-compiler/src/context.rs new file mode 100644 index 0000000..0164a51 --- /dev/null +++ b/huia-compiler/src/context.rs @@ -0,0 +1,130 @@ +use crate::error::CompileError; +use crate::location::Location; +use crate::stable::{StringIdx, StringTable}; +use crate::ty::{Ty, TyIdx}; +use huia_parser::input_location::InputLocation; + +#[derive(Debug)] +pub struct Context { + errors: Vec, + strings: StringTable, + types: Vec, + current_file: StringIdx, +} + +impl Context { + pub fn new(path: &str) -> Context { + let errors = Vec::default(); + let mut strings = StringTable::default(); + let types = Vec::default(); + let current_file = strings.intern(path); + Context { + errors, + strings, + types, + current_file, + } + } + + #[cfg(test)] + pub fn test() -> Context { + Context::new("Test context") + } + + pub fn constant_string(&mut self, value: &str) -> StringIdx { + self.strings.intern(value) + } + + pub fn declare_type( + &mut self, + name: &StringIdx, + properties: Vec<(StringIdx, TyIdx)>, + location: Location, + ) -> TyIdx { + let idx = self.types.len(); + let ty = Ty::new_type(name.clone(), location, properties); + self.types.push(ty); + idx.into() + } + + pub fn declare_trait( + &mut self, + name: &StringIdx, + requirements: Vec, + location: Location, + ) -> TyIdx { + let idx = self.types.len(); + let ty = Ty::new_trait(Some(name.clone()), location, requirements); + self.types.push(ty); + idx.into() + } + + pub fn anonymous_trait(&mut self, requirements: Vec, location: Location) -> TyIdx { + let idx = self.types.len(); + let ty = Ty::new_trait(None, location, requirements); + self.types.push(ty); + idx.into() + } + + pub fn reference_type(&mut self, name: &StringIdx, location: Location) -> TyIdx { + // Try and eagerly deference types if we can. + // This can theoretically return another type reference, so we still + // have to do a full type-check. + for (i, ty) in self.types.iter().enumerate() { + if ty.name().is_some() && &ty.name().unwrap() == name { + return i.into(); + } + } + + // Otherwise create a new type reference. + let idx = self.types.len(); + let ty = Ty::new_reference(name.clone(), location); + self.types.push(ty); + idx.into() + } + + pub fn get_type(&self, idx: &TyIdx) -> Option<&Ty> { + self.types.get(usize::from(idx)) + } + + pub fn location(&self, location: &InputLocation) -> Location { + let path = self.strings.get(self.current_file.clone()).unwrap(); + Location::new(location.clone(), path) + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_types() { + let mut context = Context::test(); + let s0 = context.constant_string("Marty McFly"); + let s1 = context.constant_string("Doc Brown"); + + let t0 = context.declare_type(&s0, Vec::new(), Location::test()); + let t1 = context.reference_type(&s0, Location::test()); + let t2 = context.reference_type(&s1, Location::test()); + + assert_eq!(t0, t1); + assert_ne!(t0, t2); + + let ty0 = context.get_type(&t0).unwrap(); + let ty1 = context.get_type(&t1).unwrap(); + let ty2 = context.get_type(&t2).unwrap(); + + assert_eq!(ty0, ty1); + assert_ne!(ty0, ty2); + + assert!(ty0.is_type()); + assert!(!ty0.is_trait()); + assert!(!ty0.is_reference()); + assert!(ty1.is_type()); + assert!(!ty1.is_trait()); + assert!(!ty1.is_reference()); + assert!(!ty2.is_type()); + assert!(!ty2.is_trait()); + assert!(ty2.is_reference()); + } +} diff --git a/huia-compiler/src/error.rs b/huia-compiler/src/error.rs new file mode 100644 index 0000000..225ca4e --- /dev/null +++ b/huia-compiler/src/error.rs @@ -0,0 +1,21 @@ +use crate::location::Location; +use std::error::Error; +use std::fmt; + +#[derive(Debug)] +pub struct CompileError { + message: String, + location: Location, +} + +impl fmt::Display for CompileError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Compile Error: {:?}", self) + } +} + +impl Error for CompileError { + fn description(&self) -> &str { + &self.message + } +} diff --git a/huia-compiler/src/ir/bb.rs b/huia-compiler/src/ir/bb.rs deleted file mode 100644 index 13c017b..0000000 --- a/huia-compiler/src/ir/bb.rs +++ /dev/null @@ -1,21 +0,0 @@ -use crate::ir::Ir; - -#[derive(Debug, Clone)] -pub struct BasicBlock { - statements: Vec, - terminator: Terminator, -} - -impl BasicBlock { - pub fn new() -> BasicBlock { - BasicBlock { - statements: Vec::default(), - terminator: Terminator::Invalid, - } - } -} - -#[derive(Debug, Clone)] -pub enum Terminator { - Invalid, -} diff --git a/huia-compiler/src/ir/builder.rs b/huia-compiler/src/ir/builder.rs new file mode 100644 index 0000000..ef6baf8 --- /dev/null +++ b/huia-compiler/src/ir/builder.rs @@ -0,0 +1,359 @@ +use crate::context::Context; +use crate::ir::{Val, IR}; +use huia_parser::ast::{Location, NodeType, Term, Value}; + +/// The Builder is a simple stack machine for converting AST into IR. +#[derive(Default)] +struct Builder { + /// The IR stack - used to compose intermediate values. + stack: Vec, +} + +impl Builder { + /// Consume an AST node and build IR. + /// + /// Any IR will be on the Builder's stack. + pub fn build(&mut self, node: Term, mut context: &mut Context) { + match node.node_type() { + 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 idx = context.constant_string(node.atom().unwrap().value_ref().as_str()); + self.stack.push(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))); + } + 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))); + } + 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))); + } + 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))); + } + 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)); + } + 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 elements = node + .array() + .unwrap() + .iter() + .map(|node| { + self.build(node.clone(), &mut context); + self.pop().unwrap() + }) + .collect(); + self.stack.push(IR::Constant(ty, Val::Array(elements))); + } + 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 elements = node + .map() + .unwrap() + .iter() + .map(|(key, value)| { + self.build(key.clone(), &mut context); + let key = self.pop().unwrap(); + self.build(value.clone(), &mut context); + let value = self.pop().unwrap(); + (key, value) + }) + .collect(); + self.stack.push(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)))); + } + 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 elements = props + .iter() + .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(); + (key, value) + }) + .collect(); + self.stack.push(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))); + } + NodeType::Call => { + let (name, args) = node.call().unwrap(); + let name = context.constant_string(name.value_ref().as_str()); + + let elements = args + .iter() + .map(|node| { + self.build(node.clone(), &mut context); + self.pop().unwrap() + }) + .collect(); + + self.stack.push(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))); + } + NodeType::TypeDef => { + let (ty, properties, body) = node.typedef().unwrap(); + let ty_idx = context.constant_string(ty.value_ref().as_str()); + let location = context.location(&node.location()); + + let properties = properties + .iter() + .map(|(key, value)| { + let key = context.constant_string(key.value_ref().as_str()); + let location = context.location(&value.location()); + let types = value + .value_ref() + .iter() + .map(|node| { + let name = context.constant_string(node.value_ref().as_str()); + let location = context.location(&node.location()); + context.reference_type(&name, location) + }) + .collect(); + let ty = context.anonymous_trait(types, location); + (key, ty) + }) + .collect(); + + 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)); + } + NodeType::TraitDef => { + let (ty, typespec, body) = node.traitdef().unwrap(); + let ty_idx = context.constant_string(ty.value_ref().as_str()); + let location = context.location(&node.location()); + + let types = if typespec.is_some() { + typespec + .unwrap() + .value_ref() + .iter() + .map(|node| { + let name = context.constant_string(node.value_ref().as_str()); + let location = context.location(&node.location()); + context.reference_type(&name, location) + }) + .collect() + } else { + Vec::new() + }; + + 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)); + } + _ => (), + } + } + + pub fn pop(&mut self) -> Option { + self.stack.pop() + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_atom() { + let term = Term::input(":marty").unwrap()[0].clone(); + let mut builder = Builder::default(); + let mut context = Context::test(); + builder.build(term, &mut context); + let ir = builder.pop().unwrap(); + assert!(ir.is_constant()); + } + + #[test] + fn test_boolean() { + let term = Term::input("true").unwrap()[0].clone(); + let mut builder = Builder::default(); + let mut context = Context::test(); + builder.build(term, &mut context); + let ir = builder.pop().unwrap(); + assert!(ir.is_constant()); + } + + #[test] + fn test_float() { + let term = Term::input("1.23").unwrap()[0].clone(); + let mut builder = Builder::default(); + let mut context = Context::test(); + builder.build(term, &mut context); + let ir = builder.pop().unwrap(); + assert!(ir.is_constant()); + } + + #[test] + fn test_string() { + let term = Term::input(r#" "Marty McFly" "#).unwrap()[0].clone(); + let mut builder = Builder::default(); + let mut context = Context::test(); + builder.build(term, &mut context); + let ir = builder.pop().unwrap(); + assert!(ir.is_constant()); + } + + #[test] + fn test_type_reference() { + let term = Term::input(r#" MartyMcFly "#).unwrap()[0].clone(); + let mut builder = Builder::default(); + let mut context = Context::test(); + builder.build(term, &mut context); + let ir = builder.pop().unwrap(); + assert!(ir.is_type_reference()); + } + + #[test] + fn test_array() { + let term = Term::input(r#" [1, 2, 3] "#).unwrap()[0].clone(); + let mut builder = Builder::default(); + let mut context = Context::test(); + builder.build(term, &mut context); + let ir = builder.pop().unwrap(); + assert!(ir.is_constant()); + } + + #[test] + fn test_map() { + let term = Term::input(r#" { a: 2, :b => 3 } "#).unwrap()[0].clone(); + let mut builder = Builder::default(); + let mut context = Context::test(); + builder.build(term, &mut context); + let ir = builder.pop().unwrap(); + assert!(ir.is_constant()); + } + + #[test] + fn test_infix() { + let term = Term::input(r#" 1 + 2 "#).unwrap()[0].clone(); + let mut builder = Builder::default(); + let mut context = Context::test(); + builder.build(term, &mut context); + let ir = builder.pop().unwrap(); + assert!(ir.is_infix()); + } + + #[test] + fn test_constructor() { + let term = Term::input(r#" Delorean { speed: 88 } "#).unwrap()[0].clone(); + let mut builder = Builder::default(); + let mut context = Context::test(); + builder.build(term, &mut context); + let ir = builder.pop().unwrap(); + assert!(ir.is_constuctor()); + } + + #[test] + fn test_unary() { + let term = Term::input(r#" +88 "#).unwrap()[0].clone(); + let mut builder = Builder::default(); + let mut context = Context::test(); + builder.build(term, &mut context); + let ir = builder.pop().unwrap(); + assert!(ir.is_unary()); + } + + #[test] + fn test_call() { + let term = Term::input(r#" x(123) "#).unwrap()[0].clone(); + let mut builder = Builder::default(); + let mut context = Context::test(); + builder.build(term, &mut context); + let ir = builder.pop().unwrap(); + assert!(ir.is_call()); + } + + #[test] + fn test_declaration() { + let term = Term::input(r#" let x = 123 "#).unwrap()[0].clone(); + let mut builder = Builder::default(); + let mut context = Context::test(); + builder.build(term, &mut context); + let ir = builder.pop().unwrap(); + assert!(ir.is_declaration()); + } + + #[test] + fn test_type_definition() { + let term = Term::file("type Delorean(speed: Integer)").unwrap()[0].clone(); + let mut builder = Builder::default(); + let mut context = Context::test(); + builder.build(term, &mut context); + let ir = builder.pop().unwrap(); + assert!(ir.is_type_definition()); + } + + #[test] + fn test_trait_definition() { + let term = Term::file("trait TimeMachine").unwrap()[0].clone(); + let mut builder = Builder::default(); + let mut context = Context::test(); + builder.build(term, &mut context); + let ir = builder.pop().unwrap(); + assert!(ir.is_trait_definition()); + } + +} diff --git a/huia-compiler/src/ir/constants.rs b/huia-compiler/src/ir/constants.rs deleted file mode 100644 index 37df139..0000000 --- a/huia-compiler/src/ir/constants.rs +++ /dev/null @@ -1,53 +0,0 @@ -use string_interner::{StringInterner, Sym}; - -/// Constant values specified in the code. -#[derive(Debug, Clone, Default)] -pub struct Constants { - strings: StringInterner, - integers: Vec, - floats: Vec, - booleans: Vec, -} - -/// An index into our constants for reference. -#[derive(Debug, Clone, PartialEq)] -pub enum ConstantIdx { - String(Sym), - Integer(usize), - Floats(usize), - Boolean(usize), -} - -impl Constants { - /// Do we have any constants stored? - pub fn is_empty(&self) -> bool { - self.strings.is_empty() && self.integers.is_empty() && self.floats.is_empty() - } - - /// Add a constant string. - pub fn push_string(&mut self, s: &str) -> ConstantIdx { - let sym = self.strings.get_or_intern(s); - ConstantIdx::String(sym) - } - - /// Add a constant integer. - pub fn push_integer(&mut self, i: i64) -> ConstantIdx { - let idx = self.integers.len(); - self.integers.push(i); - ConstantIdx::Integer(idx) - } - - /// Add a constant float. - pub fn push_float(&mut self, f: f64) -> ConstantIdx { - let idx = self.floats.len(); - self.floats.push(f); - ConstantIdx::Floats(idx) - } - - /// Add a constant boolean. - pub fn push_boolean(&mut self, b: bool) -> ConstantIdx { - let idx = self.booleans.len(); - self.booleans.push(b); - ConstantIdx::Boolean(idx) - } -} diff --git a/huia-compiler/src/ir/context.rs b/huia-compiler/src/ir/context.rs deleted file mode 100644 index bdee649..0000000 --- a/huia-compiler/src/ir/context.rs +++ /dev/null @@ -1,408 +0,0 @@ -use huia_parser::ast::{unary, Location, NodeType, Term, Value}; -use huia_parser::input_location::InputLocation; - -use crate::ir::constants::{ConstantIdx, Constants}; -use crate::ir::modules::{Module, ModuleIdx, Modules}; -use crate::ir::ty::{Ty, TyIdx, TyKind, Types}; -use crate::ir::{Ir, IrKind}; - -#[derive(Debug, Clone, Default)] -pub struct Context { - pub constants: Constants, - pub types: Types, - // it should be an error to try and consume any terms unless there is a - // current module. - modules: Modules, -} - -impl Context { - pub fn bootstrap() -> Context { - let mut context = Context::default(); - - // It's very important that the basic types are inserted such that their - // indexes are the same as those in the `TyKind` enum. If I could - // iterate them then I would. - context.types.declare( - TyKind::Atom, - context.constants.push_string("Huia.Native.Atom"), - ); - context.types.declare( - TyKind::Boolean, - context.constants.push_string("Huia.Native.Boolean"), - ); - context.types.declare( - TyKind::Float, - context.constants.push_string("Huia.Native.Float"), - ); - context.types.declare( - TyKind::Integer, - context.constants.push_string("Huia.Native.Integer"), - ); - context.types.declare( - TyKind::String, - context.constants.push_string("Huia.Native.String"), - ); - - context.types.declare( - TyKind::Array, - context.constants.push_string("Huia.Native.Array"), - ); - context.types.declare( - TyKind::Map, - context.constants.push_string("Huia.Native.Map"), - ); - - context - } - - pub fn push_module(&mut self, name: &str) -> ModuleIdx { - let cidx = self.constants.push_string(name); - self.modules.push(cidx) - } - - pub fn consume(&mut self, term: &Term) -> Ir { - assert!( - !self.modules.is_empty(), - "Cannot consume the AST without an active module." - ); - - match term.node_type() { - // Basic types. - NodeType::Atom => { - let idx = self - .constants - .push_string(term.atom().unwrap().value_ref().as_str()); - Ir::new_constant(idx, TyKind::Atom, term.location().clone()) - } - NodeType::Boolean => { - let idx = self - .constants - .push_boolean(*term.boolean().unwrap().value_ref()); - Ir::new_constant(idx, TyKind::Boolean, term.location().clone()) - } - NodeType::Float => { - let idx = self - .constants - .push_float(*term.float().unwrap().value_ref()); - Ir::new_constant(idx, TyKind::Float, term.location().clone()) - } - NodeType::Integer => { - let idx = self - .constants - .push_integer(*term.integer().unwrap().value_ref()); - Ir::new_constant(idx, TyKind::Integer, term.location().clone()) - } - NodeType::String => { - let idx = self - .constants - .push_string(term.string().unwrap().value_ref().as_str()); - Ir::new_constant(idx, TyKind::String, term.location().clone()) - } - NodeType::Ty => { - let idx = self - .constants - .push_string(term.ty().unwrap().value_ref().as_str()); - let ty = self.types.declare(TyKind::Variable, idx); - Ir::new_type_ref(ty, term.location().clone()) - } - - // Compound types - NodeType::Array => { - let elements = term - .array() - .unwrap() - .iter() - .map(|t| self.consume(t)) - .collect(); - Ir::new_constant_array(TyKind::Array, elements, term.location().clone()) - } - NodeType::Map => { - let elements = term - .map() - .unwrap() - .iter() - .map(|(k, v)| (self.consume(k), self.consume(v))) - .collect(); - Ir::new_constant_map(TyKind::Map, elements, term.location().clone()) - } - - // Expressions. - NodeType::Binary => { - let (op, lhs, rhs) = term.binary().unwrap(); - let lhs = self.consume(lhs); - let rhs = self.consume(rhs); - Ir::new_binary(op.value_ref().clone(), lhs, rhs, term.location().clone()) - } - NodeType::Constructor => { - let (ty, properties) = term.constructor().unwrap(); - - let idx = self.constants.push_string(ty.value_ref().as_str()); - let ty = self.types.declare(TyKind::Variable, idx); - - let props = properties - .iter() - .map(|(key, value)| { - let key = self.constants.push_string(key.value_ref().as_str()); - let value = self.consume(value); - (key, value) - }) - .collect(); - Ir::new_instance(ty, props, term.location().clone()) - } - NodeType::Unary => { - let (op, rhs) = term.unary().unwrap(); - let rhs = self.consume(rhs); - Ir::new_unary(op.value_ref().clone(), rhs, term.location().clone()) - } - - NodeType::TypeDef => { - let (ty, props, body) = term.typedef().unwrap(); - let idx = self.constants.push_string(ty.value_ref().as_str()); - let tyidx = self.types.declare(TyKind::UserType, idx); - - let props: Vec<(ConstantIdx, TyIdx)> = props - .iter() - .map(|(key, typespec)| { - let keyidx = self.constants.push_string(key.value_ref().as_str()); - let tyidxs: Vec = typespec - .value_ref() - .iter() - .map(|ty| { - let idx = self.constants.push_string(ty.value_ref().as_str()); - self.types.declare(TyKind::Variable, idx) - }) - .collect(); - let tyidx = self.types.spec(tyidxs); - (keyidx, tyidx) - }) - .collect(); - - let ty = self.types.get_mut(tyidx).unwrap(); - for (keyidx, tyidx) in props { - ty.set_property(keyidx, tyidx); - } - - println!("ty = {:?}", ty); - - panic!("wat"); - } - - // // Type and variable declarations. - // NodeType::Definition => { - // let (ident, term, props, body, reqs) = term.definition().unwrap(); - - // println!("body = {:?}", body); - - // let deftype = ident.value_ref().as_str(); - // match deftype { - // "type" => { - // let type_name = term.ty().unwrap().value_ref().as_str(); - // let cidx = self.constants.push_string(type_name); - // let tidx = self.types.declare(TyKind::UserType, cidx); - - // if !props.is_empty() { - // let props: Vec<(ConstantIdx, TyIdx)> = props - // .iter() - // .map(|(ident, tyref)| { - // let cidx0 = - // self.constants.push_string(ident.value_ref().as_str()); - // let cidx1 = self - // .constants - // .push_string(tyref.ty().unwrap().value_ref().as_str()); - // let tidx = self.types.declare(TyKind::Variable, cidx1); - // (cidx0, tidx) - // }) - // .collect(); - - // let ty = self.types.get_mut(tidx.clone()).unwrap(); - // for (cidx, tidx) in props { - // ty.set_property(cidx, tidx); - // } - // } - - // if !body.is_empty() { - // let body: Vec<(ConstantIdx, Vec)> = body - // .iter() - // .map(|(ident, block)| { - // let cidx = - // self.constants.push_string(ident.value_ref().as_str()); - // let block = - // block.iter().map(|term| self.consume(term)).collect(); - // (cidx, block) - // }) - // .collect(); - - // let ty = self.types.get_mut(tidx.clone()).unwrap(); - // } - - // panic!("wat"); - // } - // _ => panic!("Unexpected deftype {:?}", deftype), - // } - // } - // NodeType::Declaration => { - // let (identifier, term) = term.declaration().unwrap(); - // } - _ => panic!("unexpected node type {:?}", term.node_type()), - } - } -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn atom_literal() { - let terms = Term::input(r#" :marty "#).unwrap(); - let term = &terms[0]; - - let mut context = Context::bootstrap(); - context.push_module("test environment"); - let ir = context.consume(term); - assert!(ir.is_constant()); - assert_eq!(ir.ty(), TyIdx::from(TyKind::Atom)); - } - - #[test] - fn boolean_literals() { - { - let terms = Term::input(r#" true "#).unwrap(); - let term = &terms[0]; - - let mut context = Context::bootstrap(); - context.push_module("test environment"); - let ir = context.consume(term); - assert!(ir.is_constant()); - assert_eq!(ir.ty(), TyIdx::from(TyKind::Boolean)); - } - { - let terms = Term::input(r#" false "#).unwrap(); - let term = &terms[0]; - - let mut context = Context::bootstrap(); - context.push_module("test environment"); - let ir = context.consume(term); - assert!(ir.is_constant()); - assert_eq!(ir.ty(), TyIdx::from(TyKind::Boolean)); - } - } - - #[test] - fn float_literal() { - let terms = Term::input(r#" 1.23 "#).unwrap(); - let term = &terms[0]; - - let mut context = Context::bootstrap(); - context.push_module("test environment"); - let ir = context.consume(term); - assert!(ir.is_constant()); - assert_eq!(ir.ty(), TyIdx::from(TyKind::Float)); - } - - #[test] - fn integer_literal() { - let terms = Term::input(r#" 123 "#).unwrap(); - let term = &terms[0]; - - let mut context = Context::bootstrap(); - context.push_module("test environment"); - let ir = context.consume(term); - assert!(ir.is_constant()); - assert_eq!(ir.ty(), TyIdx::from(TyKind::Integer)); - } - - #[test] - fn string_literal() { - let terms = Term::input(r#" "Marty McFly" "#).unwrap(); - let term = &terms[0]; - - let mut context = Context::bootstrap(); - context.push_module("test environment"); - let ir = context.consume(term); - assert!(ir.is_constant()); - assert_eq!(ir.ty(), TyIdx::from(TyKind::String)); - } - - #[test] - fn type_literal() { - let terms = Term::input(r#" MartyMcFly "#).unwrap(); - let term = &terms[0]; - - let mut context = Context::bootstrap(); - context.push_module("test environment"); - let ir = context.consume(term); - assert!(ir.is_constant()); - let ty = context.types.get(ir.ty()).unwrap(); - assert_eq!(ty.kind(), TyKind::Variable); - } - - #[test] - fn array_literal() { - let terms = Term::input(r#" [1, 2, 3] "#).unwrap(); - let term = &terms[0]; - - let mut context = Context::bootstrap(); - context.push_module("test environment"); - let ir = context.consume(term); - assert!(ir.is_constant()); - assert!(ir.is_compound()); - assert_eq!(ir.ty(), TyIdx::from(TyKind::Array)); - } - - #[test] - fn map_literal() { - let terms = Term::input(r#" { a: 1, b: 2 } "#).unwrap(); - let term = &terms[0]; - - let mut context = Context::bootstrap(); - context.push_module("test environment"); - let ir = context.consume(term); - assert!(ir.is_constant()); - assert!(ir.is_compound()); - assert_eq!(ir.ty(), TyIdx::from(TyKind::Map)); - } - - #[test] - fn unary() { - let terms = Term::input(r#" -(:a) "#).unwrap(); - let term = &terms[0]; - - let mut context = Context::bootstrap(); - context.push_module("test environment"); - let ir = context.consume(term); - assert!(ir.is_unary()); - } - - #[test] - fn binary() { - let terms = Term::input(r#" 1 + 2 "#).unwrap(); - let term = &terms[0]; - - let mut context = Context::bootstrap(); - context.push_module("test environment"); - let ir = context.consume(term); - assert!(ir.is_binary()) - } - - #[test] - fn constructor() { - let terms = Term::input(r#" Delorean { speed: 88, mr_fusion: 72.6 } "#).unwrap(); - let term = &terms[0]; - - let mut context = Context::bootstrap(); - context.push_module("test environment"); - let ir = context.consume(term); - assert!(ir.is_instance()) - } - - #[test] - fn define_type() { - let terms = Term::file("type Delorean(speed: Integer) do end").unwrap(); - let term = &terms[0]; - - let mut context = Context::bootstrap(); - context.push_module("test environment"); - let ir = context.consume(term); - } -} diff --git a/huia-compiler/src/ir/functions.rs b/huia-compiler/src/ir/functions.rs deleted file mode 100644 index e81c9cc..0000000 --- a/huia-compiler/src/ir/functions.rs +++ /dev/null @@ -1,41 +0,0 @@ -use crate::ir::constants::ConstantIdx; -use crate::ir::context::Context; - -#[derive(Debug, Clone, Default)] -pub struct Function { - function_name: Option, -} - -#[derive(Debug, Clone)] -pub struct FunctionIdx(usize); - -impl From for FunctionIdx { - fn from(i: usize) -> FunctionIdx { - FunctionIdx(i) - } -} - -impl From for usize { - fn from(i: FunctionIdx) -> usize { - i.0 - } -} - -#[derive(Debug, Clone, Default)] -pub struct Functions(Vec); - -impl Functions { - pub fn push(&mut self, function: Function) -> FunctionIdx { - let i = self.0.len(); - self.0.push(function); - FunctionIdx::from(i) - } - - pub fn get>(&self, idx: T) -> Option<&Function> { - self.0.get(idx.into()) - } - - pub fn is_empty(&self) -> bool { - self.0.is_empty() - } -} diff --git a/huia-compiler/src/ir/mod.rs b/huia-compiler/src/ir/mod.rs index 0394ae7..b0134d6 100644 --- a/huia-compiler/src/ir/mod.rs +++ b/huia-compiler/src/ir/mod.rs @@ -1,158 +1,97 @@ -use huia_parser::ast::{binary, unary}; -use huia_parser::input_location::InputLocation; +mod builder; -mod bb; -mod constants; -mod context; -mod functions; -mod modules; -mod ty; +use crate::stable::StringIdx; +use crate::ty::TyIdx; +use huia_parser::ast::binary::Operator as BinOp; +use huia_parser::ast::unary::Operator as UnOp; -use constants::ConstantIdx; -use ty::TyIdx; - -#[derive(Debug, Clone)] -pub struct Ir { - pub kind: IrKind, - pub ty: TyIdx, - pub input_location: InputLocation, +/// Describes the intermediate representation as converted from the Parser's AST. +#[derive(Debug)] +pub enum IR { + Constant(TyIdx, Val), + TypeReference(TyIdx), + Infix(BinOp, Box<(IR, IR)>), + Constructor(TyIdx, Vec<(StringIdx, IR)>), + Unary(UnOp, Box), + Call(StringIdx, Vec), + Declaration(StringIdx, Box), + TypeDefinition(TyIdx), + TraitDefinition(TyIdx), } -impl Ir { - pub fn new_constant>( - idx: ConstantIdx, - ty: T, - input_location: InputLocation, - ) -> Ir { - Ir { - kind: IrKind::Constant(idx), - ty: ty.into(), - input_location, - } - } - - pub fn new_constant_array>( - ty: T, - elements: Vec, - input_location: InputLocation, - ) -> Ir { - Ir { - kind: IrKind::Array(elements), - ty: ty.into(), - input_location, - } - } - - pub fn new_constant_map>( - ty: T, - elements: Vec<(Ir, Ir)>, - input_location: InputLocation, - ) -> Ir { - Ir { - kind: IrKind::Map(elements), - ty: ty.into(), - input_location, - } - } - - pub fn new_unary(op: unary::Operator, rhs: Ir, input_location: InputLocation) -> Ir { - let ty = rhs.ty.clone(); - Ir { - kind: IrKind::Unary(op, Box::new(rhs)), - ty, - input_location, - } - } - - pub fn new_binary(op: binary::Operator, lhs: Ir, rhs: Ir, input_location: InputLocation) -> Ir { - // Massive assumption that the result of addition is the same type as - // the LHS value. - let ty = lhs.ty.clone(); - Ir { - kind: IrKind::Binary(op, Box::new(lhs), Box::new(rhs)), - ty, - input_location, - } - } - - pub fn new_type_ref(ty: TyIdx, input_location: InputLocation) -> Ir { - Ir { - kind: IrKind::TypeRef, - ty, - input_location, - } - } - - pub fn new_instance( - ty: TyIdx, - properties: Vec<(ConstantIdx, Ir)>, - input_location: InputLocation, - ) -> Ir { - Ir { - kind: IrKind::Instance(properties), - ty, - input_location, - } - } - - pub fn ty(&self) -> TyIdx { - self.ty.clone() - } - +impl IR { pub fn is_constant(&self) -> bool { - match self.kind { - IrKind::Constant(_) => true, - IrKind::Array(_) => true, - IrKind::Map(_) => true, - IrKind::TypeRef => true, + match self { + IR::Constant(..) => true, _ => false, } } - pub fn is_compound(&self) -> bool { - match self.kind { - IrKind::Array(_) => true, - IrKind::Map(_) => true, + pub fn is_type_reference(&self) -> bool { + match self { + IR::TypeReference(..) => true, + _ => false, + } + } + + pub fn is_infix(&self) -> bool { + match self { + IR::Infix(..) => true, + _ => false, + } + } + + pub fn is_constuctor(&self) -> bool { + match self { + IR::Constructor(..) => true, _ => false, } } pub fn is_unary(&self) -> bool { - match self.kind { - IrKind::Unary(_, _) => true, + match self { + IR::Unary(..) => true, _ => false, } } - pub fn is_binary(&self) -> bool { - match self.kind { - IrKind::Binary(_, _, _) => true, + pub fn is_call(&self) -> bool { + match self { + IR::Call(..) => true, _ => false, } } - pub fn is_type_ref(&self) -> bool { - match self.kind { - IrKind::TypeRef => true, + pub fn is_declaration(&self) -> bool { + match self { + IR::Declaration(..) => true, _ => false, } } - pub fn is_instance(&self) -> bool { - match self.kind { - IrKind::Instance(_) => true, + pub fn is_type_definition(&self) -> bool { + match self { + IR::TypeDefinition(..) => true, + _ => false, + } + } + + pub fn is_trait_definition(&self) -> bool { + match self { + IR::TraitDefinition(..) => true, _ => false, } } } -#[derive(Debug, Clone)] -pub enum IrKind { - Constant(ConstantIdx), - Array(Vec), - Map(Vec<(Ir, Ir)>), - Unary(unary::Operator, Box), - Binary(binary::Operator, Box, Box), - TypeRef, - Instance(Vec<(ConstantIdx, Ir)>), +/// A constant value. +#[derive(Debug)] +pub enum Val { + Atom(StringIdx), + Boolean(bool), + Float(f64), + Integer(i64), + String(StringIdx), + Array(Vec), + Map(Vec<(IR, IR)>), } diff --git a/huia-compiler/src/ir/modules.rs b/huia-compiler/src/ir/modules.rs deleted file mode 100644 index 7ce5db1..0000000 --- a/huia-compiler/src/ir/modules.rs +++ /dev/null @@ -1,42 +0,0 @@ -use crate::ir::constants::ConstantIdx; -use crate::ir::context::Context; - -#[derive(Debug, Clone)] -pub struct Module { - file_name: ConstantIdx, -} - -#[derive(Debug, Clone)] -pub struct ModuleIdx(usize); - -impl From for ModuleIdx { - fn from(i: usize) -> ModuleIdx { - ModuleIdx(i) - } -} - -impl From for usize { - fn from(i: ModuleIdx) -> usize { - i.0 - } -} - -#[derive(Debug, Clone, Default)] -pub struct Modules(Vec); - -impl Modules { - pub fn push(&mut self, file_name: ConstantIdx) -> ModuleIdx { - let module = Module { file_name }; - let i = self.0.len(); - self.0.push(module); - ModuleIdx::from(i) - } - - pub fn get>(&self, idx: T) -> Option<&Module> { - self.0.get(idx.into()) - } - - pub fn is_empty(&self) -> bool { - self.0.is_empty() - } -} diff --git a/huia-compiler/src/ir/ty.rs b/huia-compiler/src/ir/ty.rs deleted file mode 100644 index fd8e6c4..0000000 --- a/huia-compiler/src/ir/ty.rs +++ /dev/null @@ -1,211 +0,0 @@ -use crate::ir::constants::ConstantIdx; -use crate::ir::functions::Functions; - -#[derive(Debug, Clone)] -pub struct Ty { - kind: TyKind, - data: TyData, -} - -impl Ty { - pub fn kind(&self) -> TyKind { - self.kind.clone() - } - - pub fn name(&self) -> Option { - match self.data { - TyData::Type { ref name, .. } => Some(name.clone()), - TyData::Trait { ref name, .. } => Some(name.clone()), - _ => None, - } - } - - pub fn set_property(&mut self, key: ConstantIdx, value: TyIdx) { - match &mut self.data { - TyData::Type { properties, .. } => properties.push((key, value)), - _ => panic!("Cannot add properties to {:?} types", self.data), - } - } -} - -#[derive(Debug, Clone, PartialEq, PartialOrd)] -pub enum TyKind { - // Basic Types - Atom = 0, - Boolean, - Float, - Integer, - String, - - // Compound types, - Array, - Map, - - // User defined types. - UserTrait, - UserType, - - // Type variable for an unresolved type during compilation. - Variable, - - // Compound type for typesoecs; - Compound, - - // A type error. - Error, -} - -#[derive(Debug, Clone)] -pub enum TyData { - Type { - name: ConstantIdx, - properties: Vec<(ConstantIdx, TyIdx)>, - }, - Trait { - name: ConstantIdx, - corequires: Vec, - }, - Native { - name: ConstantIdx, - }, - Spec(Vec), -} - -#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord)] -pub struct TyIdx(usize); - -impl From for TyIdx { - fn from(i: usize) -> TyIdx { - TyIdx(i) - } -} - -impl From for TyIdx { - fn from(kind: TyKind) -> TyIdx { - assert!(kind < TyKind::UserTrait, "Cannot imply type for {:?}", kind); - TyIdx(kind as usize) - } -} - -impl From for usize { - fn from(i: TyIdx) -> usize { - i.0 - } -} - -#[derive(Debug, Clone, Default)] -pub struct Types(Vec); - -impl Types { - pub fn declare(&mut self, kind: TyKind, name: ConstantIdx) -> TyIdx { - // Look for an existing type with the same name. - for (i, ty) in self.0.iter().enumerate() { - match ty.data { - TyData::Native { name: ref n } => { - if n == &name { - return TyIdx::from(i); - } - } - TyData::Type { name: ref n, .. } => { - if n == &name { - return TyIdx::from(i); - } - } - TyData::Trait { name: ref n, .. } => { - if n == &name { - return TyIdx::from(i); - } - } - _ => (), - } - } - - // Otherwise add a new type. - let i = self.0.len(); - let data = match kind { - TyKind::Atom => TyData::Native { name }, - TyKind::Boolean => TyData::Native { name }, - TyKind::Float => TyData::Native { name }, - TyKind::Integer => TyData::Native { name }, - TyKind::String => TyData::Native { name }, - TyKind::Array => TyData::Native { name }, - TyKind::Map => TyData::Native { name }, - TyKind::UserTrait => TyData::Trait { - name, - corequires: Vec::new(), - }, - TyKind::UserType => TyData::Type { - name, - properties: Vec::new(), - }, - TyKind::Variable => TyData::Native { name }, - _ => panic!("Cannot declare {:?} type", kind), - }; - let ty = Ty { kind, data }; - self.0.push(ty); - TyIdx::from(i) - } - - pub fn spec(&mut self, mut idxs: Vec) -> TyIdx { - idxs.sort(); - // Look for an existing typespec with the same types. - - for (i, ty) in self.0.iter().enumerate() { - match &ty.data { - TyData::Spec(tys) => { - if tys == &idxs { - return TyIdx::from(i); - } - } - _ => (), - } - } - - let i = self.0.len(); - let data = TyData::Spec(idxs); - let kind = TyKind::Compound; - let ty = Ty { kind, data }; - self.0.push(ty); - TyIdx::from(i) - } - - pub fn get>(&self, idx: T) -> Option<&Ty> { - self.0.get(idx.into()) - } - - pub fn get_mut>(&mut self, idx: T) -> Option<&mut Ty> { - self.0.get_mut(idx.into()) - } -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn declare_test() { - let name0 = ConstantIdx::Integer(0); - let name1 = ConstantIdx::Integer(1); - - let mut types = Types::default(); - - let idx0 = types.declare(TyKind::Integer, name0.clone()); - let idx1 = types.declare(TyKind::Integer, name1); - let idx2 = types.declare(TyKind::Integer, name0); - - assert_eq!(idx0, idx2); - assert_ne!(idx0, idx1); - } - - #[test] - fn spec_test() { - let mut types = Types::default(); - - let name0 = ConstantIdx::Integer(0); - let name1 = ConstantIdx::Integer(1); - let idx0 = types.declare(TyKind::Integer, name0); - let idx1 = types.declare(TyKind::Integer, name1); - - types.spec(vec![idx0, idx1]); - } -} diff --git a/huia-compiler/src/lib.rs b/huia-compiler/src/lib.rs index 5b6c825..fc53442 100644 --- a/huia-compiler/src/lib.rs +++ b/huia-compiler/src/lib.rs @@ -1 +1,6 @@ +mod context; +mod error; mod ir; +mod location; +mod stable; +mod ty; diff --git a/huia-compiler/src/location.rs b/huia-compiler/src/location.rs new file mode 100644 index 0000000..f1379c5 --- /dev/null +++ b/huia-compiler/src/location.rs @@ -0,0 +1,31 @@ +use huia_parser::input_location::InputLocation; +use std::fmt; + +#[derive(Debug, Clone, PartialEq)] +pub struct Location { + location: InputLocation, + source_path: String, +} + +impl Location { + pub fn new(location: InputLocation, source_path: &str) -> Location { + Location { + location, + source_path: source_path.to_string(), + } + } + + #[cfg(test)] + pub fn test() -> Location { + Location { + location: InputLocation::pos(0), + source_path: String::from("Test Example"), + } + } +} + +impl fmt::Display for Location { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}:{}", self.source_path, self.location) + } +} diff --git a/huia-compiler/src/stable.rs b/huia-compiler/src/stable.rs new file mode 100644 index 0000000..16b26fd --- /dev/null +++ b/huia-compiler/src/stable.rs @@ -0,0 +1,61 @@ +use string_interner::{StringInterner, Sym}; + +#[derive(Debug, Default)] +pub struct StringTable(StringInterner); + +impl StringTable { + pub fn intern(&mut self, value: &str) -> StringIdx { + self.0.get_or_intern(value).into() + } + + pub fn get(&self, idx: StringIdx) -> Option<&str> { + self.0.resolve(idx.into()) + } + + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } +} + +#[derive(Debug, Clone, PartialEq)] +pub struct StringIdx(Sym); + +impl From for StringIdx { + fn from(s: Sym) -> StringIdx { + StringIdx(s) + } +} + +impl From for Sym { + fn from(s: StringIdx) -> Sym { + s.0 + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_default() { + let stable = StringTable::default(); + assert!(stable.is_empty()); + } + + #[test] + fn test_insert() { + let mut stable = StringTable::default(); + assert!(stable.is_empty()); + + let idx0 = stable.intern("Marty McFly"); + let idx1 = stable.intern("Marty McFly"); + assert_eq!(idx0, idx1); + } + + #[test] + fn test_get() { + let mut stable = StringTable::default(); + let idx = stable.intern("Marty McFly"); + assert_eq!(stable.get(idx).unwrap(), "Marty McFly"); + } +} diff --git a/huia-compiler/src/ty.rs b/huia-compiler/src/ty.rs new file mode 100644 index 0000000..99dc05e --- /dev/null +++ b/huia-compiler/src/ty.rs @@ -0,0 +1,122 @@ +use crate::location::Location; +use crate::stable::StringIdx; +use std::fmt; + +#[derive(Debug, PartialEq)] +pub struct Ty { + name: Option, + location: Location, + kind: TyKind, + inner: TyInner, +} + +impl Ty { + pub fn new_type( + name: StringIdx, + location: Location, + properties: Vec<(StringIdx, TyIdx)>, + ) -> Ty { + Ty { + name: Some(name), + location, + kind: TyKind::Type, + inner: TyInner::Type { properties }, + } + } + + pub fn new_trait(name: Option, location: Location, dependencies: Vec) -> Ty { + Ty { + name, + location, + kind: TyKind::Trait, + inner: TyInner::Trait { dependencies }, + } + } + + pub fn new_reference(name: StringIdx, location: Location) -> Ty { + Ty { + name: Some(name), + location, + kind: TyKind::Unresolved, + inner: TyInner::Empty, + } + } + + pub fn name(&self) -> Option { + self.name.clone() + } + + pub fn is_type(&self) -> bool { + match self.kind { + TyKind::Native => true, + TyKind::Type => true, + _ => false, + } + } + + pub fn is_trait(&self) -> bool { + match self.kind { + TyKind::Trait => true, + _ => false, + } + } + + pub fn is_reference(&self) -> bool { + match self.kind { + TyKind::Unresolved => true, + _ => false, + } + } +} + +#[derive(Debug, Clone, PartialEq, PartialOrd, Ord, Eq)] +pub struct TyIdx(usize); + +impl From for TyIdx { + fn from(i: usize) -> TyIdx { + TyIdx(i) + } +} + +impl From for usize { + fn from(i: TyIdx) -> usize { + i.0 + } +} + +impl From<&TyIdx> for usize { + fn from(i: &TyIdx) -> usize { + i.0 + } +} + +#[derive(Debug, PartialEq)] +pub enum TyKind { + Native, + Type, + Trait, + Unresolved, +} + +impl fmt::Display for TyKind { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + TyKind::Native => write!(f, "native type"), + TyKind::Type => write!(f, "type"), + TyKind::Trait => write!(f, "trait"), + TyKind::Unresolved => write!(f, "unresolved type"), + } + } +} + +#[derive(Debug, Clone, PartialEq)] +enum TyInner { + Type { properties: Vec<(StringIdx, TyIdx)> }, + Trait { dependencies: Vec }, + NativeInteger, + NativeFloat, + NativeString, + NativeArray, + NativeMap, + Empty, +} diff --git a/huia-parser/src/ast/atom.rs b/huia-parser/src/ast/atom.rs index f8ecaef..0404b57 100644 --- a/huia-parser/src/ast/atom.rs +++ b/huia-parser/src/ast/atom.rs @@ -3,7 +3,7 @@ use crate::grammar::Rule; use crate::input_location::InputLocation; use pest::iterators::Pair; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub struct Atom { value: String, location: InputLocation, diff --git a/huia-parser/src/ast/binary.rs b/huia-parser/src/ast/binary.rs index 019fa10..bcc096f 100644 --- a/huia-parser/src/ast/binary.rs +++ b/huia-parser/src/ast/binary.rs @@ -3,7 +3,7 @@ use crate::grammar::Rule; use crate::input_location::InputLocation; use pest::iterators::Pair; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub struct Binary { value: Operator, location: InputLocation, @@ -11,7 +11,6 @@ pub struct Binary { #[derive(Debug, Clone, PartialEq)] pub enum Operator { - Assign, BitwiseAnd, BitwiseOr, BitwiseXor, @@ -55,10 +54,6 @@ impl Location for Binary { impl<'a> From> for Binary { fn from(pair: Pair<'a, Rule>) -> Self { match pair.as_rule() { - Rule::assign => Binary { - value: Operator::Assign, - location: InputLocation::from(pair.into_span()), - }, Rule::bitwise_and => Binary { value: Operator::BitwiseAnd, location: InputLocation::from(pair.into_span()), diff --git a/huia-parser/src/ast/boolean.rs b/huia-parser/src/ast/boolean.rs index 3ce9552..159ddaf 100644 --- a/huia-parser/src/ast/boolean.rs +++ b/huia-parser/src/ast/boolean.rs @@ -3,7 +3,7 @@ use crate::grammar::Rule; use crate::input_location::InputLocation; use pest::iterators::Pair; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub struct Boolean { value: bool, location: InputLocation, diff --git a/huia-parser/src/ast/float.rs b/huia-parser/src/ast/float.rs index bf22087..06d4de7 100644 --- a/huia-parser/src/ast/float.rs +++ b/huia-parser/src/ast/float.rs @@ -3,7 +3,7 @@ use crate::grammar::Rule; use crate::input_location::InputLocation; use pest::iterators::Pair; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub struct Float { value: f64, location: InputLocation, diff --git a/huia-parser/src/ast/identifier.rs b/huia-parser/src/ast/identifier.rs index 5b1ea17..77f1deb 100644 --- a/huia-parser/src/ast/identifier.rs +++ b/huia-parser/src/ast/identifier.rs @@ -3,7 +3,7 @@ use crate::grammar::Rule; use crate::input_location::InputLocation; use pest::iterators::Pair; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub struct Identifier { value: String, location: InputLocation, @@ -52,7 +52,10 @@ impl<'a> From> for Identifier { location: InputLocation::from(pair.into_span()), } } - _ => unreachable!("Expected pair to be an Identifier (received {:?})", pair.as_rule()), + _ => unreachable!( + "Expected pair to be an Identifier (received {:?})", + pair.as_rule() + ), } } } diff --git a/huia-parser/src/ast/integer.rs b/huia-parser/src/ast/integer.rs index cc2fe72..864ba2c 100644 --- a/huia-parser/src/ast/integer.rs +++ b/huia-parser/src/ast/integer.rs @@ -3,7 +3,7 @@ use crate::grammar::Rule; use crate::input_location::InputLocation; use pest::iterators::Pair; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub struct Integer { value: i64, radix: usize, diff --git a/huia-parser/src/ast/local.rs b/huia-parser/src/ast/local.rs index 912cb0a..697eebb 100644 --- a/huia-parser/src/ast/local.rs +++ b/huia-parser/src/ast/local.rs @@ -3,7 +3,7 @@ use crate::grammar::Rule; use crate::input_location::InputLocation; use pest::iterators::Pair; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub struct Local { value: String, location: InputLocation, diff --git a/huia-parser/src/ast/string.rs b/huia-parser/src/ast/string.rs index bb2ee4a..5e32f96 100644 --- a/huia-parser/src/ast/string.rs +++ b/huia-parser/src/ast/string.rs @@ -4,7 +4,7 @@ use crate::input_location::InputLocation; use pest::iterators::Pair; use std::string; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub struct String { value: string::String, location: InputLocation, diff --git a/huia-parser/src/ast/term.rs b/huia-parser/src/ast/term.rs index 4e880fb..64c539e 100644 --- a/huia-parser/src/ast/term.rs +++ b/huia-parser/src/ast/term.rs @@ -10,7 +10,7 @@ use pest::Parser; // terminals and the term enum handles mashing them together with their children // for compound nodes. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub struct Term { location: InputLocation, inner: Inner, @@ -37,7 +37,6 @@ pub enum NodeType { Call, Declaration, - Definition, Local, TypeDef, @@ -48,7 +47,7 @@ pub enum NodeType { StaticMethod, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] enum Inner { Array(Vec), Atom(Atom), @@ -66,9 +65,24 @@ enum Inner { TypeDef(Ty, Vec<(Identifier, TypeSpec)>, Vec), TraitDef(Ty, Option, Vec), ImplDef(Ty, Vec), - PublicMethod(Identifier, Vec<(Identifier, TypeSpec)>, Option, Vec), - PrivateMethod(Identifier, Vec<(Identifier, TypeSpec)>, Option, Vec), - StaticMethod(Identifier, Vec<(Identifier, TypeSpec)>, Option, Vec), + PublicMethod( + Identifier, + Vec<(Identifier, TypeSpec)>, + Option, + Vec, + ), + PrivateMethod( + Identifier, + Vec<(Identifier, TypeSpec)>, + Option, + Vec, + ), + StaticMethod( + Identifier, + Vec<(Identifier, TypeSpec)>, + Option, + Vec, + ), Unary(Unary, Box), } @@ -185,42 +199,69 @@ impl Term { pub fn traitdef(&self) -> Option<(&Ty, Option<&TypeSpec>, &Vec)> { match self.inner { Inner::TraitDef(ref ty, ref reqs, ref body) => Some((ty, reqs.as_ref(), body)), - _ => None + _ => None, } } pub fn typedef(&self) -> Option<(&Ty, &Vec<(Identifier, TypeSpec)>, &Vec)> { match self.inner { Inner::TypeDef(ref ty, ref props, ref body) => Some((ty, props, body)), - _ => None + _ => None, } } pub fn impldef(&self) -> Option<(&Ty, &Vec)> { match self.inner { Inner::ImplDef(ref ty, ref body) => Some((ty, body)), - _ => None + _ => None, } } - pub fn public_method(&self) -> Option<(&Identifier, &Vec<(Identifier, TypeSpec)>, Option<&TypeSpec>, &Vec)> { + pub fn public_method( + &self, + ) -> Option<( + &Identifier, + &Vec<(Identifier, TypeSpec)>, + Option<&TypeSpec>, + &Vec, + )> { match self.inner { - Inner::PublicMethod(ref name, ref args, ref rval, ref body) => Some((name, args, rval.as_ref(), body)), - _ => None + Inner::PublicMethod(ref name, ref args, ref rval, ref body) => { + Some((name, args, rval.as_ref(), body)) + } + _ => None, } } - pub fn private_method(&self) -> Option<(&Identifier, &Vec<(Identifier, TypeSpec)>, Option<&TypeSpec>, &Vec)> { + pub fn private_method( + &self, + ) -> Option<( + &Identifier, + &Vec<(Identifier, TypeSpec)>, + Option<&TypeSpec>, + &Vec, + )> { match self.inner { - Inner::PrivateMethod(ref name, ref args, ref rval, ref body) => Some((name, args, rval.as_ref(), body)), - _ => None + Inner::PrivateMethod(ref name, ref args, ref rval, ref body) => { + Some((name, args, rval.as_ref(), body)) + } + _ => None, } } - pub fn static_method(&self) -> Option<(&Identifier, &Vec<(Identifier, TypeSpec)>, Option<&TypeSpec>, &Vec)> { + pub fn static_method( + &self, + ) -> Option<( + &Identifier, + &Vec<(Identifier, TypeSpec)>, + Option<&TypeSpec>, + &Vec, + )> { match self.inner { - Inner::StaticMethod(ref name, ref args, ref rval, ref body) => Some((name, args, rval.as_ref(), body)), - _ => None + Inner::StaticMethod(ref name, ref args, ref rval, ref body) => { + Some((name, args, rval.as_ref(), body)) + } + _ => None, } } @@ -404,105 +445,125 @@ impl<'a> From> for Term { location: InputLocation::from(pair), inner: Inner::Unary(operator, Box::new(rhs)), } - }, + } Rule::traitdef => { let mut inner = pair.clone().into_inner(); let ty = Ty::from(inner.next().unwrap()); let reqs = match inner.next().unwrap().into_inner().next() { Some(p) => Some(TypeSpec::from(p)), - None => None + None => None, }; - let block = inner.next().unwrap().into_inner().map(|p| Term::from(p)).collect(); + let block = inner.next().unwrap().into_inner().map(Term::from).collect(); Term { location: InputLocation::from(pair), - inner: Inner::TraitDef(ty, reqs, block) + inner: Inner::TraitDef(ty, reqs, block), } - }, + } Rule::typedef => { let mut inner = pair.clone().into_inner(); let ty = Ty::from(inner.next().unwrap()); - let props = inner.next().unwrap().into_inner().map(|p| { - let mut inner = p.into_inner(); - let keyword = Identifier::from(inner.next().unwrap()); - let typespec = TypeSpec::from(inner.next().unwrap()); - (keyword, typespec) - }).collect(); - let block = inner.next().unwrap().into_inner().map(|p| Term::from(p)).collect(); - Term{ + let props = inner + .next() + .unwrap() + .into_inner() + .map(|p| { + let mut inner = p.into_inner(); + let keyword = Identifier::from(inner.next().unwrap()); + let typespec = TypeSpec::from(inner.next().unwrap()); + (keyword, typespec) + }) + .collect(); + let block = inner.next().unwrap().into_inner().map(Term::from).collect(); + Term { location: InputLocation::from(pair), - inner: Inner::TypeDef(ty, props, block) + inner: Inner::TypeDef(ty, props, block), } } Rule::impldef => { let mut inner = pair.clone().into_inner(); let ty = Ty::from(inner.next().unwrap()); - let block = inner.next().unwrap().into_inner().map(|p| Term::from(p)).collect(); + let block = inner.next().unwrap().into_inner().map(Term::from).collect(); Term { location: InputLocation::from(pair), - inner: Inner::ImplDef(ty, block) + inner: Inner::ImplDef(ty, block), } } Rule::defpublic => { let mut inner = pair.clone().into_inner(); let name = Identifier::from(inner.next().unwrap()); - let args = inner.next().unwrap().into_inner().map(|p| { - let mut inner = p.into_inner(); - let keyword = Identifier::from(inner.next().unwrap()); - let typespec = TypeSpec::from(inner.next().unwrap()); - (keyword, typespec) - }).collect(); + let args = inner + .next() + .unwrap() + .into_inner() + .map(|p| { + let mut inner = p.into_inner(); + let keyword = Identifier::from(inner.next().unwrap()); + let typespec = TypeSpec::from(inner.next().unwrap()); + (keyword, typespec) + }) + .collect(); let rval = match inner.next().unwrap().into_inner().next() { Some(value) => Some(TypeSpec::from(value)), - _ => None + _ => None, }; - let block = inner.next().unwrap().into_inner().map(|p| Term::from(p)).collect(); - Term{ + let block = inner.next().unwrap().into_inner().map(Term::from).collect(); + Term { location: InputLocation::from(pair), - inner: Inner::PublicMethod(name, args, rval, block) + inner: Inner::PublicMethod(name, args, rval, block), } - }, + } Rule::defprivate => { let mut inner = pair.clone().into_inner(); let name = Identifier::from(inner.next().unwrap()); - let args = inner.next().unwrap().into_inner().map(|p| { - let mut inner = p.into_inner(); - let keyword = Identifier::from(inner.next().unwrap()); - let typespec = TypeSpec::from(inner.next().unwrap()); - (keyword, typespec) - }).collect(); + let args = inner + .next() + .unwrap() + .into_inner() + .map(|p| { + let mut inner = p.into_inner(); + let keyword = Identifier::from(inner.next().unwrap()); + let typespec = TypeSpec::from(inner.next().unwrap()); + (keyword, typespec) + }) + .collect(); let rval = match inner.next().unwrap().into_inner().next() { Some(value) => Some(TypeSpec::from(value)), - _ => None + _ => None, }; - let block = inner.next().unwrap().into_inner().map(|p| Term::from(p)).collect(); - Term{ + let block = inner.next().unwrap().into_inner().map(Term::from).collect(); + Term { location: InputLocation::from(pair), - inner: Inner::PrivateMethod(name, args, rval, block) + inner: Inner::PrivateMethod(name, args, rval, block), } - }, + } Rule::defstatic => { let mut inner = pair.clone().into_inner(); let name = Identifier::from(inner.next().unwrap()); - let args = inner.next().unwrap().into_inner().map(|p| { - let mut inner = p.into_inner(); - let keyword = Identifier::from(inner.next().unwrap()); - let typespec = TypeSpec::from(inner.next().unwrap()); - (keyword, typespec) - }).collect(); + let args = inner + .next() + .unwrap() + .into_inner() + .map(|p| { + let mut inner = p.into_inner(); + let keyword = Identifier::from(inner.next().unwrap()); + let typespec = TypeSpec::from(inner.next().unwrap()); + (keyword, typespec) + }) + .collect(); let rval = match inner.next().unwrap().into_inner().next() { Some(value) => Some(TypeSpec::from(value)), - _ => None + _ => None, }; - let block = inner.next().unwrap().into_inner().map(|p| Term::from(p)).collect(); - Term{ + let block = inner.next().unwrap().into_inner().map(Term::from).collect(); + Term { location: InputLocation::from(pair), - inner: Inner::StaticMethod(name, args, rval, block) + inner: Inner::StaticMethod(name, args, rval, block), } - }, + } _ => panic!("Unexpected pair {:?}", pair), } @@ -650,11 +711,14 @@ mod test { #[test] fn test_typedef_with_impl() { - let terms = Term::file(r#" + let terms = Term::file( + r#" type Delorean(speed: Integer) do impl TimeMachine end - "#).unwrap(); + "#, + ) + .unwrap(); let (ty, props, body) = terms[0].typedef().unwrap(); assert_eq!(ty.value_ref(), "Delorean"); assert_eq!(props.len(), 1); @@ -671,13 +735,16 @@ mod test { #[test] fn test_typdef_with_methods() { - let terms = Term::file(r#" + let terms = Term::file( + r#" type Delorean(speed: Integer) do defs new() do Delorean { speed: 0 } end end - "#).unwrap(); + "#, + ) + .unwrap(); let (ty, props, body) = terms[0].typedef().unwrap(); assert_eq!(ty.value_ref(), "Delorean"); assert_eq!(props.len(), 1); @@ -687,7 +754,6 @@ mod test { assert_eq!(key.value_ref(), "speed"); assert_eq!(value.value_ref()[0].value_ref(), "Integer"); - let (name, args, rval, block) = &body[0].static_method().unwrap(); assert_eq!(name.value_ref(), "new"); assert!(args.is_empty()); @@ -695,7 +761,6 @@ mod test { assert_eq!(block.len(), 1); } - #[test] fn test_declaration() { let terms = Term::input("let speed = 88").unwrap(); diff --git a/huia-parser/src/ast/ty.rs b/huia-parser/src/ast/ty.rs index 49d1d99..da7a7d7 100644 --- a/huia-parser/src/ast/ty.rs +++ b/huia-parser/src/ast/ty.rs @@ -3,7 +3,7 @@ use crate::grammar::Rule; use crate::input_location::InputLocation; use pest::iterators::Pair; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub struct Ty { value: String, location: InputLocation, diff --git a/huia-parser/src/ast/typespec.rs b/huia-parser/src/ast/typespec.rs index c206f66..112831a 100644 --- a/huia-parser/src/ast/typespec.rs +++ b/huia-parser/src/ast/typespec.rs @@ -1,9 +1,9 @@ -use crate::ast::{Location, Value, Ty}; +use crate::ast::{Location, Ty, Value}; use crate::grammar::Rule; use crate::input_location::InputLocation; use pest::iterators::Pair; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub struct TypeSpec { value: Vec, location: InputLocation, @@ -31,14 +31,13 @@ impl<'a> From> for TypeSpec { fn from(pair: Pair<'a, Rule>) -> Self { match pair.as_rule() { Rule::typespec => { - let value = pair.clone().into_inner().map(|p| Ty::from(p)).collect(); + let value = pair.clone().into_inner().map(Ty::from).collect(); TypeSpec { value, location: InputLocation::from(pair.into_span()), } - - }, - _ => unreachable!("Expected pair to be an TypeSpec"), + } + _ => unreachable!("Expected pair to be an TypeSpec"), } } } diff --git a/huia-parser/src/ast/unary.rs b/huia-parser/src/ast/unary.rs index 5cbc73b..3a5c0ce 100644 --- a/huia-parser/src/ast/unary.rs +++ b/huia-parser/src/ast/unary.rs @@ -3,7 +3,7 @@ use crate::grammar::Rule; use crate::input_location::InputLocation; use pest::iterators::Pair; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub struct Unary { value: Operator, location: InputLocation, diff --git a/huia-parser/src/input_location.rs b/huia-parser/src/input_location.rs index 52305d4..184be0a 100644 --- a/huia-parser/src/input_location.rs +++ b/huia-parser/src/input_location.rs @@ -1,7 +1,8 @@ use crate::grammar::Rule; use pest; +use std::fmt; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub enum InputLocation { Pos(usize), Span(usize, usize), @@ -43,3 +44,12 @@ impl<'a> From> for InputLocation { InputLocation::from(pair.into_span()) } } + +impl fmt::Display for InputLocation { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + InputLocation::Pos(pos) => write!(f, "{}", pos), + InputLocation::Span(start, end) => write!(f, "{}:{}", start, end), + } + } +}