Use a simple stack machine to build the IR.

This commit is contained in:
James Harton 2019-03-13 20:32:13 +13:00
parent 81c0ac85c4
commit f6359f4245
27 changed files with 962 additions and 998 deletions

View file

@ -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<CompileError>,
strings: StringTable,
types: Vec<Ty>,
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<TyIdx>,
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<TyIdx>, 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());
}
}

View file

@ -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
}
}

View file

@ -1,21 +0,0 @@
use crate::ir::Ir;
#[derive(Debug, Clone)]
pub struct BasicBlock {
statements: Vec<Ir>,
terminator: Terminator,
}
impl BasicBlock {
pub fn new() -> BasicBlock {
BasicBlock {
statements: Vec::default(),
terminator: Terminator::Invalid,
}
}
}
#[derive(Debug, Clone)]
pub enum Terminator {
Invalid,
}

View file

@ -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<IR>,
}
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<IR> {
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());
}
}

View file

@ -1,53 +0,0 @@
use string_interner::{StringInterner, Sym};
/// Constant values specified in the code.
#[derive(Debug, Clone, Default)]
pub struct Constants {
strings: StringInterner<Sym>,
integers: Vec<i64>,
floats: Vec<f64>,
booleans: Vec<bool>,
}
/// 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)
}
}

View file

@ -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<TyIdx> = 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<Ir>)> = 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);
}
}

View file

@ -1,41 +0,0 @@
use crate::ir::constants::ConstantIdx;
use crate::ir::context::Context;
#[derive(Debug, Clone, Default)]
pub struct Function {
function_name: Option<ConstantIdx>,
}
#[derive(Debug, Clone)]
pub struct FunctionIdx(usize);
impl From<usize> for FunctionIdx {
fn from(i: usize) -> FunctionIdx {
FunctionIdx(i)
}
}
impl From<FunctionIdx> for usize {
fn from(i: FunctionIdx) -> usize {
i.0
}
}
#[derive(Debug, Clone, Default)]
pub struct Functions(Vec<Function>);
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<T: Into<usize>>(&self, idx: T) -> Option<&Function> {
self.0.get(idx.into())
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
}

View file

@ -1,158 +1,97 @@
use huia_parser::ast::{binary, unary}; mod builder;
use huia_parser::input_location::InputLocation;
mod bb; use crate::stable::StringIdx;
mod constants; use crate::ty::TyIdx;
mod context; use huia_parser::ast::binary::Operator as BinOp;
mod functions; use huia_parser::ast::unary::Operator as UnOp;
mod modules;
mod ty;
use constants::ConstantIdx; /// Describes the intermediate representation as converted from the Parser's AST.
use ty::TyIdx; #[derive(Debug)]
pub enum IR {
#[derive(Debug, Clone)] Constant(TyIdx, Val),
pub struct Ir { TypeReference(TyIdx),
pub kind: IrKind, Infix(BinOp, Box<(IR, IR)>),
pub ty: TyIdx, Constructor(TyIdx, Vec<(StringIdx, IR)>),
pub input_location: InputLocation, Unary(UnOp, Box<IR>),
} Call(StringIdx, Vec<IR>),
Declaration(StringIdx, Box<IR>),
impl Ir { TypeDefinition(TyIdx),
pub fn new_constant<T: Into<TyIdx>>( TraitDefinition(TyIdx),
idx: ConstantIdx,
ty: T,
input_location: InputLocation,
) -> Ir {
Ir {
kind: IrKind::Constant(idx),
ty: ty.into(),
input_location,
}
}
pub fn new_constant_array<T: Into<TyIdx>>(
ty: T,
elements: Vec<Ir>,
input_location: InputLocation,
) -> Ir {
Ir {
kind: IrKind::Array(elements),
ty: ty.into(),
input_location,
}
}
pub fn new_constant_map<T: Into<TyIdx>>(
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 { pub fn is_constant(&self) -> bool {
match self.kind { match self {
IrKind::Constant(_) => true, IR::Constant(..) => true,
IrKind::Array(_) => true,
IrKind::Map(_) => true,
IrKind::TypeRef => true,
_ => false, _ => false,
} }
} }
pub fn is_compound(&self) -> bool { pub fn is_type_reference(&self) -> bool {
match self.kind { match self {
IrKind::Array(_) => true, IR::TypeReference(..) => true,
IrKind::Map(_) => 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, _ => false,
} }
} }
pub fn is_unary(&self) -> bool { pub fn is_unary(&self) -> bool {
match self.kind { match self {
IrKind::Unary(_, _) => true, IR::Unary(..) => true,
_ => false, _ => false,
} }
} }
pub fn is_binary(&self) -> bool { pub fn is_call(&self) -> bool {
match self.kind { match self {
IrKind::Binary(_, _, _) => true, IR::Call(..) => true,
_ => false, _ => false,
} }
} }
pub fn is_type_ref(&self) -> bool { pub fn is_declaration(&self) -> bool {
match self.kind { match self {
IrKind::TypeRef => true, IR::Declaration(..) => true,
_ => false, _ => false,
} }
} }
pub fn is_instance(&self) -> bool { pub fn is_type_definition(&self) -> bool {
match self.kind { match self {
IrKind::Instance(_) => true, IR::TypeDefinition(..) => true,
_ => false,
}
}
pub fn is_trait_definition(&self) -> bool {
match self {
IR::TraitDefinition(..) => true,
_ => false, _ => false,
} }
} }
} }
#[derive(Debug, Clone)] /// A constant value.
pub enum IrKind { #[derive(Debug)]
Constant(ConstantIdx), pub enum Val {
Array(Vec<Ir>), Atom(StringIdx),
Map(Vec<(Ir, Ir)>), Boolean(bool),
Unary(unary::Operator, Box<Ir>), Float(f64),
Binary(binary::Operator, Box<Ir>, Box<Ir>), Integer(i64),
TypeRef, String(StringIdx),
Instance(Vec<(ConstantIdx, Ir)>), Array(Vec<IR>),
Map(Vec<(IR, IR)>),
} }

View file

@ -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<usize> for ModuleIdx {
fn from(i: usize) -> ModuleIdx {
ModuleIdx(i)
}
}
impl From<ModuleIdx> for usize {
fn from(i: ModuleIdx) -> usize {
i.0
}
}
#[derive(Debug, Clone, Default)]
pub struct Modules(Vec<Module>);
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<T: Into<usize>>(&self, idx: T) -> Option<&Module> {
self.0.get(idx.into())
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
}

View file

@ -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<ConstantIdx> {
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<TyIdx>,
},
Native {
name: ConstantIdx,
},
Spec(Vec<TyIdx>),
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord)]
pub struct TyIdx(usize);
impl From<usize> for TyIdx {
fn from(i: usize) -> TyIdx {
TyIdx(i)
}
}
impl From<TyKind> for TyIdx {
fn from(kind: TyKind) -> TyIdx {
assert!(kind < TyKind::UserTrait, "Cannot imply type for {:?}", kind);
TyIdx(kind as usize)
}
}
impl From<TyIdx> for usize {
fn from(i: TyIdx) -> usize {
i.0
}
}
#[derive(Debug, Clone, Default)]
pub struct Types(Vec<Ty>);
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>) -> 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<T: Into<usize>>(&self, idx: T) -> Option<&Ty> {
self.0.get(idx.into())
}
pub fn get_mut<T: Into<usize>>(&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]);
}
}

View file

@ -1 +1,6 @@
mod context;
mod error;
mod ir; mod ir;
mod location;
mod stable;
mod ty;

View file

@ -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)
}
}

View file

@ -0,0 +1,61 @@
use string_interner::{StringInterner, Sym};
#[derive(Debug, Default)]
pub struct StringTable(StringInterner<Sym>);
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<Sym> for StringIdx {
fn from(s: Sym) -> StringIdx {
StringIdx(s)
}
}
impl From<StringIdx> 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");
}
}

122
huia-compiler/src/ty.rs Normal file
View file

@ -0,0 +1,122 @@
use crate::location::Location;
use crate::stable::StringIdx;
use std::fmt;
#[derive(Debug, PartialEq)]
pub struct Ty {
name: Option<StringIdx>,
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<StringIdx>, location: Location, dependencies: Vec<TyIdx>) -> 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<StringIdx> {
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<usize> for TyIdx {
fn from(i: usize) -> TyIdx {
TyIdx(i)
}
}
impl From<TyIdx> 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<TyIdx> },
NativeInteger,
NativeFloat,
NativeString,
NativeArray,
NativeMap,
Empty,
}

View file

@ -3,7 +3,7 @@ use crate::grammar::Rule;
use crate::input_location::InputLocation; use crate::input_location::InputLocation;
use pest::iterators::Pair; use pest::iterators::Pair;
#[derive(Debug, Clone)] #[derive(Debug, Clone, PartialEq)]
pub struct Atom { pub struct Atom {
value: String, value: String,
location: InputLocation, location: InputLocation,

View file

@ -3,7 +3,7 @@ use crate::grammar::Rule;
use crate::input_location::InputLocation; use crate::input_location::InputLocation;
use pest::iterators::Pair; use pest::iterators::Pair;
#[derive(Debug, Clone)] #[derive(Debug, Clone, PartialEq)]
pub struct Binary { pub struct Binary {
value: Operator, value: Operator,
location: InputLocation, location: InputLocation,
@ -11,7 +11,6 @@ pub struct Binary {
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub enum Operator { pub enum Operator {
Assign,
BitwiseAnd, BitwiseAnd,
BitwiseOr, BitwiseOr,
BitwiseXor, BitwiseXor,
@ -55,10 +54,6 @@ impl Location for Binary {
impl<'a> From<Pair<'a, Rule>> for Binary { impl<'a> From<Pair<'a, Rule>> for Binary {
fn from(pair: Pair<'a, Rule>) -> Self { fn from(pair: Pair<'a, Rule>) -> Self {
match pair.as_rule() { match pair.as_rule() {
Rule::assign => Binary {
value: Operator::Assign,
location: InputLocation::from(pair.into_span()),
},
Rule::bitwise_and => Binary { Rule::bitwise_and => Binary {
value: Operator::BitwiseAnd, value: Operator::BitwiseAnd,
location: InputLocation::from(pair.into_span()), location: InputLocation::from(pair.into_span()),

View file

@ -3,7 +3,7 @@ use crate::grammar::Rule;
use crate::input_location::InputLocation; use crate::input_location::InputLocation;
use pest::iterators::Pair; use pest::iterators::Pair;
#[derive(Debug, Clone)] #[derive(Debug, Clone, PartialEq)]
pub struct Boolean { pub struct Boolean {
value: bool, value: bool,
location: InputLocation, location: InputLocation,

View file

@ -3,7 +3,7 @@ use crate::grammar::Rule;
use crate::input_location::InputLocation; use crate::input_location::InputLocation;
use pest::iterators::Pair; use pest::iterators::Pair;
#[derive(Debug, Clone)] #[derive(Debug, Clone, PartialEq)]
pub struct Float { pub struct Float {
value: f64, value: f64,
location: InputLocation, location: InputLocation,

View file

@ -3,7 +3,7 @@ use crate::grammar::Rule;
use crate::input_location::InputLocation; use crate::input_location::InputLocation;
use pest::iterators::Pair; use pest::iterators::Pair;
#[derive(Debug, Clone)] #[derive(Debug, Clone, PartialEq)]
pub struct Identifier { pub struct Identifier {
value: String, value: String,
location: InputLocation, location: InputLocation,
@ -52,7 +52,10 @@ impl<'a> From<Pair<'a, Rule>> for Identifier {
location: InputLocation::from(pair.into_span()), 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()
),
} }
} }
} }

View file

@ -3,7 +3,7 @@ use crate::grammar::Rule;
use crate::input_location::InputLocation; use crate::input_location::InputLocation;
use pest::iterators::Pair; use pest::iterators::Pair;
#[derive(Debug, Clone)] #[derive(Debug, Clone, PartialEq)]
pub struct Integer { pub struct Integer {
value: i64, value: i64,
radix: usize, radix: usize,

View file

@ -3,7 +3,7 @@ use crate::grammar::Rule;
use crate::input_location::InputLocation; use crate::input_location::InputLocation;
use pest::iterators::Pair; use pest::iterators::Pair;
#[derive(Debug, Clone)] #[derive(Debug, Clone, PartialEq)]
pub struct Local { pub struct Local {
value: String, value: String,
location: InputLocation, location: InputLocation,

View file

@ -4,7 +4,7 @@ use crate::input_location::InputLocation;
use pest::iterators::Pair; use pest::iterators::Pair;
use std::string; use std::string;
#[derive(Debug, Clone)] #[derive(Debug, Clone, PartialEq)]
pub struct String { pub struct String {
value: string::String, value: string::String,
location: InputLocation, location: InputLocation,

View file

@ -10,7 +10,7 @@ use pest::Parser;
// terminals and the term enum handles mashing them together with their children // terminals and the term enum handles mashing them together with their children
// for compound nodes. // for compound nodes.
#[derive(Debug, Clone)] #[derive(Debug, Clone, PartialEq)]
pub struct Term { pub struct Term {
location: InputLocation, location: InputLocation,
inner: Inner, inner: Inner,
@ -37,7 +37,6 @@ pub enum NodeType {
Call, Call,
Declaration, Declaration,
Definition,
Local, Local,
TypeDef, TypeDef,
@ -48,7 +47,7 @@ pub enum NodeType {
StaticMethod, StaticMethod,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone, PartialEq)]
enum Inner { enum Inner {
Array(Vec<Term>), Array(Vec<Term>),
Atom(Atom), Atom(Atom),
@ -66,9 +65,24 @@ enum Inner {
TypeDef(Ty, Vec<(Identifier, TypeSpec)>, Vec<Term>), TypeDef(Ty, Vec<(Identifier, TypeSpec)>, Vec<Term>),
TraitDef(Ty, Option<TypeSpec>, Vec<Term>), TraitDef(Ty, Option<TypeSpec>, Vec<Term>),
ImplDef(Ty, Vec<Term>), ImplDef(Ty, Vec<Term>),
PublicMethod(Identifier, Vec<(Identifier, TypeSpec)>, Option<TypeSpec>, Vec<Term>), PublicMethod(
PrivateMethod(Identifier, Vec<(Identifier, TypeSpec)>, Option<TypeSpec>, Vec<Term>), Identifier,
StaticMethod(Identifier, Vec<(Identifier, TypeSpec)>, Option<TypeSpec>, Vec<Term>), Vec<(Identifier, TypeSpec)>,
Option<TypeSpec>,
Vec<Term>,
),
PrivateMethod(
Identifier,
Vec<(Identifier, TypeSpec)>,
Option<TypeSpec>,
Vec<Term>,
),
StaticMethod(
Identifier,
Vec<(Identifier, TypeSpec)>,
Option<TypeSpec>,
Vec<Term>,
),
Unary(Unary, Box<Term>), Unary(Unary, Box<Term>),
} }
@ -185,42 +199,69 @@ impl Term {
pub fn traitdef(&self) -> Option<(&Ty, Option<&TypeSpec>, &Vec<Term>)> { pub fn traitdef(&self) -> Option<(&Ty, Option<&TypeSpec>, &Vec<Term>)> {
match self.inner { match self.inner {
Inner::TraitDef(ref ty, ref reqs, ref body) => Some((ty, reqs.as_ref(), body)), 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<Term>)> { pub fn typedef(&self) -> Option<(&Ty, &Vec<(Identifier, TypeSpec)>, &Vec<Term>)> {
match self.inner { match self.inner {
Inner::TypeDef(ref ty, ref props, ref body) => Some((ty, props, body)), Inner::TypeDef(ref ty, ref props, ref body) => Some((ty, props, body)),
_ => None _ => None,
} }
} }
pub fn impldef(&self) -> Option<(&Ty, &Vec<Term>)> { pub fn impldef(&self) -> Option<(&Ty, &Vec<Term>)> {
match self.inner { match self.inner {
Inner::ImplDef(ref ty, ref body) => Some((ty, body)), Inner::ImplDef(ref ty, ref body) => Some((ty, body)),
_ => None _ => None,
} }
} }
pub fn public_method(&self) -> Option<(&Identifier, &Vec<(Identifier, TypeSpec)>, Option<&TypeSpec>, &Vec<Term>)> { pub fn public_method(
&self,
) -> Option<(
&Identifier,
&Vec<(Identifier, TypeSpec)>,
Option<&TypeSpec>,
&Vec<Term>,
)> {
match self.inner { match self.inner {
Inner::PublicMethod(ref name, ref args, ref rval, ref body) => Some((name, args, rval.as_ref(), body)), Inner::PublicMethod(ref name, ref args, ref rval, ref body) => {
_ => None Some((name, args, rval.as_ref(), body))
}
_ => None,
} }
} }
pub fn private_method(&self) -> Option<(&Identifier, &Vec<(Identifier, TypeSpec)>, Option<&TypeSpec>, &Vec<Term>)> { pub fn private_method(
&self,
) -> Option<(
&Identifier,
&Vec<(Identifier, TypeSpec)>,
Option<&TypeSpec>,
&Vec<Term>,
)> {
match self.inner { match self.inner {
Inner::PrivateMethod(ref name, ref args, ref rval, ref body) => Some((name, args, rval.as_ref(), body)), Inner::PrivateMethod(ref name, ref args, ref rval, ref body) => {
_ => None Some((name, args, rval.as_ref(), body))
}
_ => None,
} }
} }
pub fn static_method(&self) -> Option<(&Identifier, &Vec<(Identifier, TypeSpec)>, Option<&TypeSpec>, &Vec<Term>)> { pub fn static_method(
&self,
) -> Option<(
&Identifier,
&Vec<(Identifier, TypeSpec)>,
Option<&TypeSpec>,
&Vec<Term>,
)> {
match self.inner { match self.inner {
Inner::StaticMethod(ref name, ref args, ref rval, ref body) => Some((name, args, rval.as_ref(), body)), Inner::StaticMethod(ref name, ref args, ref rval, ref body) => {
_ => None Some((name, args, rval.as_ref(), body))
}
_ => None,
} }
} }
@ -404,105 +445,125 @@ impl<'a> From<Pair<'a, Rule>> for Term {
location: InputLocation::from(pair), location: InputLocation::from(pair),
inner: Inner::Unary(operator, Box::new(rhs)), inner: Inner::Unary(operator, Box::new(rhs)),
} }
}, }
Rule::traitdef => { Rule::traitdef => {
let mut inner = pair.clone().into_inner(); let mut inner = pair.clone().into_inner();
let ty = Ty::from(inner.next().unwrap()); let ty = Ty::from(inner.next().unwrap());
let reqs = match inner.next().unwrap().into_inner().next() { let reqs = match inner.next().unwrap().into_inner().next() {
Some(p) => Some(TypeSpec::from(p)), 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 { Term {
location: InputLocation::from(pair), location: InputLocation::from(pair),
inner: Inner::TraitDef(ty, reqs, block) inner: Inner::TraitDef(ty, reqs, block),
}
} }
},
Rule::typedef => { Rule::typedef => {
let mut inner = pair.clone().into_inner(); let mut inner = pair.clone().into_inner();
let ty = Ty::from(inner.next().unwrap()); let ty = Ty::from(inner.next().unwrap());
let props = inner.next().unwrap().into_inner().map(|p| { let props = inner
.next()
.unwrap()
.into_inner()
.map(|p| {
let mut inner = p.into_inner(); let mut inner = p.into_inner();
let keyword = Identifier::from(inner.next().unwrap()); let keyword = Identifier::from(inner.next().unwrap());
let typespec = TypeSpec::from(inner.next().unwrap()); let typespec = TypeSpec::from(inner.next().unwrap());
(keyword, typespec) (keyword, typespec)
}).collect(); })
let block = inner.next().unwrap().into_inner().map(|p| Term::from(p)).collect(); .collect();
let block = inner.next().unwrap().into_inner().map(Term::from).collect();
Term { Term {
location: InputLocation::from(pair), location: InputLocation::from(pair),
inner: Inner::TypeDef(ty, props, block) inner: Inner::TypeDef(ty, props, block),
} }
} }
Rule::impldef => { Rule::impldef => {
let mut inner = pair.clone().into_inner(); let mut inner = pair.clone().into_inner();
let ty = Ty::from(inner.next().unwrap()); 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 { Term {
location: InputLocation::from(pair), location: InputLocation::from(pair),
inner: Inner::ImplDef(ty, block) inner: Inner::ImplDef(ty, block),
} }
} }
Rule::defpublic => { Rule::defpublic => {
let mut inner = pair.clone().into_inner(); let mut inner = pair.clone().into_inner();
let name = Identifier::from(inner.next().unwrap()); let name = Identifier::from(inner.next().unwrap());
let args = inner.next().unwrap().into_inner().map(|p| { let args = inner
.next()
.unwrap()
.into_inner()
.map(|p| {
let mut inner = p.into_inner(); let mut inner = p.into_inner();
let keyword = Identifier::from(inner.next().unwrap()); let keyword = Identifier::from(inner.next().unwrap());
let typespec = TypeSpec::from(inner.next().unwrap()); let typespec = TypeSpec::from(inner.next().unwrap());
(keyword, typespec) (keyword, typespec)
}).collect(); })
.collect();
let rval = match inner.next().unwrap().into_inner().next() { let rval = match inner.next().unwrap().into_inner().next() {
Some(value) => Some(TypeSpec::from(value)), Some(value) => Some(TypeSpec::from(value)),
_ => 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 { Term {
location: InputLocation::from(pair), location: InputLocation::from(pair),
inner: Inner::PublicMethod(name, args, rval, block) inner: Inner::PublicMethod(name, args, rval, block),
}
} }
},
Rule::defprivate => { Rule::defprivate => {
let mut inner = pair.clone().into_inner(); let mut inner = pair.clone().into_inner();
let name = Identifier::from(inner.next().unwrap()); let name = Identifier::from(inner.next().unwrap());
let args = inner.next().unwrap().into_inner().map(|p| { let args = inner
.next()
.unwrap()
.into_inner()
.map(|p| {
let mut inner = p.into_inner(); let mut inner = p.into_inner();
let keyword = Identifier::from(inner.next().unwrap()); let keyword = Identifier::from(inner.next().unwrap());
let typespec = TypeSpec::from(inner.next().unwrap()); let typespec = TypeSpec::from(inner.next().unwrap());
(keyword, typespec) (keyword, typespec)
}).collect(); })
.collect();
let rval = match inner.next().unwrap().into_inner().next() { let rval = match inner.next().unwrap().into_inner().next() {
Some(value) => Some(TypeSpec::from(value)), Some(value) => Some(TypeSpec::from(value)),
_ => 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 { Term {
location: InputLocation::from(pair), location: InputLocation::from(pair),
inner: Inner::PrivateMethod(name, args, rval, block) inner: Inner::PrivateMethod(name, args, rval, block),
}
} }
},
Rule::defstatic => { Rule::defstatic => {
let mut inner = pair.clone().into_inner(); let mut inner = pair.clone().into_inner();
let name = Identifier::from(inner.next().unwrap()); let name = Identifier::from(inner.next().unwrap());
let args = inner.next().unwrap().into_inner().map(|p| { let args = inner
.next()
.unwrap()
.into_inner()
.map(|p| {
let mut inner = p.into_inner(); let mut inner = p.into_inner();
let keyword = Identifier::from(inner.next().unwrap()); let keyword = Identifier::from(inner.next().unwrap());
let typespec = TypeSpec::from(inner.next().unwrap()); let typespec = TypeSpec::from(inner.next().unwrap());
(keyword, typespec) (keyword, typespec)
}).collect(); })
.collect();
let rval = match inner.next().unwrap().into_inner().next() { let rval = match inner.next().unwrap().into_inner().next() {
Some(value) => Some(TypeSpec::from(value)), Some(value) => Some(TypeSpec::from(value)),
_ => 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 { Term {
location: InputLocation::from(pair), location: InputLocation::from(pair),
inner: Inner::StaticMethod(name, args, rval, block) inner: Inner::StaticMethod(name, args, rval, block),
}
} }
},
_ => panic!("Unexpected pair {:?}", pair), _ => panic!("Unexpected pair {:?}", pair),
} }
@ -650,11 +711,14 @@ mod test {
#[test] #[test]
fn test_typedef_with_impl() { fn test_typedef_with_impl() {
let terms = Term::file(r#" let terms = Term::file(
r#"
type Delorean(speed: Integer) do type Delorean(speed: Integer) do
impl TimeMachine impl TimeMachine
end end
"#).unwrap(); "#,
)
.unwrap();
let (ty, props, body) = terms[0].typedef().unwrap(); let (ty, props, body) = terms[0].typedef().unwrap();
assert_eq!(ty.value_ref(), "Delorean"); assert_eq!(ty.value_ref(), "Delorean");
assert_eq!(props.len(), 1); assert_eq!(props.len(), 1);
@ -671,13 +735,16 @@ mod test {
#[test] #[test]
fn test_typdef_with_methods() { fn test_typdef_with_methods() {
let terms = Term::file(r#" let terms = Term::file(
r#"
type Delorean(speed: Integer) do type Delorean(speed: Integer) do
defs new() do defs new() do
Delorean { speed: 0 } Delorean { speed: 0 }
end end
end end
"#).unwrap(); "#,
)
.unwrap();
let (ty, props, body) = terms[0].typedef().unwrap(); let (ty, props, body) = terms[0].typedef().unwrap();
assert_eq!(ty.value_ref(), "Delorean"); assert_eq!(ty.value_ref(), "Delorean");
assert_eq!(props.len(), 1); assert_eq!(props.len(), 1);
@ -687,7 +754,6 @@ mod test {
assert_eq!(key.value_ref(), "speed"); assert_eq!(key.value_ref(), "speed");
assert_eq!(value.value_ref()[0].value_ref(), "Integer"); assert_eq!(value.value_ref()[0].value_ref(), "Integer");
let (name, args, rval, block) = &body[0].static_method().unwrap(); let (name, args, rval, block) = &body[0].static_method().unwrap();
assert_eq!(name.value_ref(), "new"); assert_eq!(name.value_ref(), "new");
assert!(args.is_empty()); assert!(args.is_empty());
@ -695,7 +761,6 @@ mod test {
assert_eq!(block.len(), 1); assert_eq!(block.len(), 1);
} }
#[test] #[test]
fn test_declaration() { fn test_declaration() {
let terms = Term::input("let speed = 88").unwrap(); let terms = Term::input("let speed = 88").unwrap();

View file

@ -3,7 +3,7 @@ use crate::grammar::Rule;
use crate::input_location::InputLocation; use crate::input_location::InputLocation;
use pest::iterators::Pair; use pest::iterators::Pair;
#[derive(Debug, Clone)] #[derive(Debug, Clone, PartialEq)]
pub struct Ty { pub struct Ty {
value: String, value: String,
location: InputLocation, location: InputLocation,

View file

@ -1,9 +1,9 @@
use crate::ast::{Location, Value, Ty}; use crate::ast::{Location, Ty, Value};
use crate::grammar::Rule; use crate::grammar::Rule;
use crate::input_location::InputLocation; use crate::input_location::InputLocation;
use pest::iterators::Pair; use pest::iterators::Pair;
#[derive(Debug, Clone)] #[derive(Debug, Clone, PartialEq)]
pub struct TypeSpec { pub struct TypeSpec {
value: Vec<Ty>, value: Vec<Ty>,
location: InputLocation, location: InputLocation,
@ -31,13 +31,12 @@ impl<'a> From<Pair<'a, Rule>> for TypeSpec {
fn from(pair: Pair<'a, Rule>) -> Self { fn from(pair: Pair<'a, Rule>) -> Self {
match pair.as_rule() { match pair.as_rule() {
Rule::typespec => { 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 { TypeSpec {
value, value,
location: InputLocation::from(pair.into_span()), location: InputLocation::from(pair.into_span()),
} }
}
},
_ => unreachable!("Expected pair to be an TypeSpec"), _ => unreachable!("Expected pair to be an TypeSpec"),
} }
} }

View file

@ -3,7 +3,7 @@ use crate::grammar::Rule;
use crate::input_location::InputLocation; use crate::input_location::InputLocation;
use pest::iterators::Pair; use pest::iterators::Pair;
#[derive(Debug, Clone)] #[derive(Debug, Clone, PartialEq)]
pub struct Unary { pub struct Unary {
value: Operator, value: Operator,
location: InputLocation, location: InputLocation,

View file

@ -1,7 +1,8 @@
use crate::grammar::Rule; use crate::grammar::Rule;
use pest; use pest;
use std::fmt;
#[derive(Debug, Clone)] #[derive(Debug, Clone, PartialEq)]
pub enum InputLocation { pub enum InputLocation {
Pos(usize), Pos(usize),
Span(usize, usize), Span(usize, usize),
@ -43,3 +44,12 @@ impl<'a> From<pest::iterators::Pair<'a, Rule>> for InputLocation {
InputLocation::from(pair.into_span()) 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),
}
}
}