Use a simple stack machine to build the IR.
This commit is contained in:
parent
81c0ac85c4
commit
f6359f4245
27 changed files with 962 additions and 998 deletions
130
huia-compiler/src/context.rs
Normal file
130
huia-compiler/src/context.rs
Normal 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());
|
||||
}
|
||||
}
|
21
huia-compiler/src/error.rs
Normal file
21
huia-compiler/src/error.rs
Normal 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
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
}
|
359
huia-compiler/src/ir/builder.rs
Normal file
359
huia-compiler/src/ir/builder.rs
Normal 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());
|
||||
}
|
||||
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
}
|
||||
|
||||
impl Ir {
|
||||
pub fn new_constant<T: Into<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()
|
||||
/// 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<IR>),
|
||||
Call(StringIdx, Vec<IR>),
|
||||
Declaration(StringIdx, Box<IR>),
|
||||
TypeDefinition(TyIdx),
|
||||
TraitDefinition(TyIdx),
|
||||
}
|
||||
|
||||
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<Ir>),
|
||||
Map(Vec<(Ir, Ir)>),
|
||||
Unary(unary::Operator, Box<Ir>),
|
||||
Binary(binary::Operator, Box<Ir>, Box<Ir>),
|
||||
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<IR>),
|
||||
Map(Vec<(IR, IR)>),
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
|
@ -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]);
|
||||
}
|
||||
}
|
|
@ -1 +1,6 @@
|
|||
mod context;
|
||||
mod error;
|
||||
mod ir;
|
||||
mod location;
|
||||
mod stable;
|
||||
mod ty;
|
||||
|
|
31
huia-compiler/src/location.rs
Normal file
31
huia-compiler/src/location.rs
Normal 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)
|
||||
}
|
||||
}
|
61
huia-compiler/src/stable.rs
Normal file
61
huia-compiler/src/stable.rs
Normal 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
122
huia-compiler/src/ty.rs
Normal 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,
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -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<Pair<'a, Rule>> 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()),
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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<Pair<'a, Rule>> 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()
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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<Term>),
|
||||
Atom(Atom),
|
||||
|
@ -66,9 +65,24 @@ enum Inner {
|
|||
TypeDef(Ty, Vec<(Identifier, TypeSpec)>, Vec<Term>),
|
||||
TraitDef(Ty, Option<TypeSpec>, Vec<Term>),
|
||||
ImplDef(Ty, Vec<Term>),
|
||||
PublicMethod(Identifier, 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>),
|
||||
PublicMethod(
|
||||
Identifier,
|
||||
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>),
|
||||
}
|
||||
|
||||
|
@ -185,42 +199,69 @@ impl Term {
|
|||
pub fn traitdef(&self) -> Option<(&Ty, Option<&TypeSpec>, &Vec<Term>)> {
|
||||
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<Term>)> {
|
||||
match self.inner {
|
||||
Inner::TypeDef(ref ty, ref props, ref body) => Some((ty, props, body)),
|
||||
_ => None
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn impldef(&self) -> Option<(&Ty, &Vec<Term>)> {
|
||||
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<Term>)> {
|
||||
pub fn public_method(
|
||||
&self,
|
||||
) -> Option<(
|
||||
&Identifier,
|
||||
&Vec<(Identifier, TypeSpec)>,
|
||||
Option<&TypeSpec>,
|
||||
&Vec<Term>,
|
||||
)> {
|
||||
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<Term>)> {
|
||||
pub fn private_method(
|
||||
&self,
|
||||
) -> Option<(
|
||||
&Identifier,
|
||||
&Vec<(Identifier, TypeSpec)>,
|
||||
Option<&TypeSpec>,
|
||||
&Vec<Term>,
|
||||
)> {
|
||||
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<Term>)> {
|
||||
pub fn static_method(
|
||||
&self,
|
||||
) -> Option<(
|
||||
&Identifier,
|
||||
&Vec<(Identifier, TypeSpec)>,
|
||||
Option<&TypeSpec>,
|
||||
&Vec<Term>,
|
||||
)> {
|
||||
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<Pair<'a, Rule>> 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 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();
|
||||
})
|
||||
.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 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();
|
||||
})
|
||||
.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();
|
||||
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 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();
|
||||
})
|
||||
.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();
|
||||
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 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();
|
||||
})
|
||||
.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();
|
||||
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();
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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<Ty>,
|
||||
location: InputLocation,
|
||||
|
@ -31,13 +31,12 @@ impl<'a> From<Pair<'a, Rule>> 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"),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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<pest::iterators::Pair<'a, Rule>> 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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Reference in a new issue