Add a bunch more IR stuff and anonymous functions.

This commit is contained in:
James Harton 2019-03-14 21:25:12 +13:00
parent 781832eb7d
commit bd2177940c
10 changed files with 444 additions and 80 deletions

View file

@ -1,4 +1,6 @@
use crate::error::CompileError;
use crate::function::{Clause, Function, FunctionIdx};
use crate::ir::IR;
use crate::location::Location;
use crate::stable::{StringIdx, StringTable};
use crate::ty::{Ty, TyIdx};
@ -9,20 +11,23 @@ pub struct Context {
errors: Vec<CompileError>,
strings: StringTable,
types: Vec<Ty>,
functions: Vec<Function>,
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);
let errors = Vec::default();
let functions = Vec::default();
let types = Vec::default();
Context {
current_file,
errors,
functions,
strings,
types,
current_file,
}
}
@ -90,20 +95,37 @@ impl Context {
idx.into()
}
pub fn unknown_type(&mut self, location: Location) -> TyIdx {
let idx = self.types.len();
let ty = Ty::unknown_type(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)
}
pub fn define_function(&mut self, function: Function) -> FunctionIdx {
let idx = self.functions.len();
self.functions.push(function);
idx.into()
}
pub fn get_function(&self, idx: FunctionIdx) -> Option<&Function> {
self.functions.get(usize::from(idx))
}
#[cfg(test)]
pub fn find_type(&mut self, name: &str) -> Option<&Ty> {
let name = self.constant_string(name);
self.types.iter().find(|ty| ty.name() == Some(name.clone()))
}
pub fn location(&self, location: &InputLocation) -> Location {
let path = self.strings.get(self.current_file.clone()).unwrap();
Location::new(location.clone(), path)
}
}
#[cfg(test)]

View file

@ -0,0 +1,83 @@
use crate::ir::IR;
use crate::stable::StringIdx;
use crate::ty::TyIdx;
#[derive(Debug)]
pub struct Function {
clauses: Vec<Clause>,
return_type: TyIdx,
}
impl Function {
pub fn new(return_type: TyIdx) -> Function {
Function {
return_type,
clauses: Vec::new(),
}
}
pub fn push(&mut self, clause: Clause) {
self.clauses.push(clause);
}
pub fn is_empty(&self) -> bool {
self.clauses.is_empty()
}
}
#[derive(Debug)]
pub struct Clause {
arguments: Vec<(StringIdx, TyIdx)>,
body: Vec<IR>,
}
impl Clause {
pub fn new(arguments: Vec<(StringIdx, TyIdx)>) -> Clause {
Clause {
arguments,
body: Vec::new(),
}
}
pub fn push(&mut self, ir: IR) {
self.body.push(ir);
}
}
#[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
}
}
impl From<&FunctionIdx> for usize {
fn from(i: &FunctionIdx) -> usize {
i.0
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_function_new() {
let rt = TyIdx::from(13);
let mut fun = Function::new(rt);
assert!(fun.is_empty());
let clause = Clause::new(Vec::new());
fun.push(clause);
assert!(!fun.is_empty());
}
}

View file

@ -1,5 +1,7 @@
use crate::context::Context;
use crate::function::{Clause, Function};
use crate::ir::{Val, IR};
use crate::stable::StringIdx;
use crate::ty::TyIdx;
use huia_parser::ast::{Location, NodeType, Term, Value};
@ -14,6 +16,10 @@ struct Builder {
/// Type stack - used to track which type is currently being defined.
ty_stack: Vec<TyIdx>,
/// Function stack - used to track which function is currently being defined,
fn_stack: Vec<Function>,
clause_stack: Vec<Clause>,
}
impl Builder {
@ -232,6 +238,55 @@ impl Builder {
self.pop_ty();
}
NodeType::Function => {
let function = node.function().unwrap();
let location = context.location(&node.location());
let return_type = context.unknown_type(location);
let mut fun = Function::new(return_type);
for clause in function.value_ref() {
let arguments: Vec<(StringIdx, TyIdx)> = clause
.arguments()
.iter()
.map(|(name, type_spec)| {
let name = context.constant_string(name.value_ref().as_str());
let location = context.location(&type_spec.location());
let types = type_spec
.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);
(name, ty)
})
.collect();
// FIXME: We need to put the built nodes into a scope
// pointing at the current clause rather than just stuffing
// them into the stack where they're just sitting there
// doing no good to anyone.
self.push_clause(Clause::new(arguments));
for node in clause.body() {
self.build(node.clone(), &mut context);
}
fun.push(self.pop_clause().unwrap());
}
let idx = context.define_function(fun);
self.push_ir(IR::Function(idx));
}
// NodeType::PublicMethod => {
// let (name, args, typespec, body) = node.public_method().unwrap();
// let name = context.constant_string(name.value_ref().as_str());
// let location = context.location(&node.location());
// }
_ => (),
}
}
@ -256,6 +311,27 @@ impl Builder {
let len = self.ty_stack.len();
self.ty_stack.get(len - 1)
}
pub fn push_fn(&mut self, fun: Function) {
self.fn_stack.push(fun);
}
pub fn pop_fn(&mut self) -> Option<Function> {
self.fn_stack.pop()
}
pub fn push_clause(&mut self, clause: Clause) {
self.clause_stack.push(clause);
}
pub fn peek_clause_mut(&mut self) -> Option<&mut Clause> {
let len = self.clause_stack.len();
self.clause_stack.get_mut(len - 1)
}
pub fn pop_clause(&mut self) -> Option<Clause> {
self.clause_stack.pop()
}
}
#[cfg(test)]
@ -435,4 +511,18 @@ mod test {
assert!(context.find_type("TimeMachine").is_some());
}
#[test]
fn test_anonymous_function() {
let term = Term::input(r#"fn (speed: Integer) do "WAT" end"#).unwrap()[0].clone();
let mut builder = Builder::default();
let mut context = Context::test();
builder.build(term, &mut context);
let ir = builder.pop_ir().unwrap();
assert!(ir.is_function());
let fun = context
.get_function(ir.function().unwrap().clone())
.unwrap();
assert!(!fun.is_empty());
}
}

View file

@ -1,5 +1,6 @@
mod builder;
use crate::function::FunctionIdx;
use crate::stable::StringIdx;
use crate::ty::TyIdx;
use huia_parser::ast::binary::Operator as BinOp;
@ -18,6 +19,7 @@ pub enum IR {
Unary(UnOp, Box<IR>),
Call(StringIdx, Vec<IR>),
Declaration(StringIdx, Box<IR>),
Function(FunctionIdx),
}
impl IR {
@ -69,6 +71,20 @@ impl IR {
_ => false,
}
}
pub fn is_function(&self) -> bool {
match self {
IR::Function(..) => true,
_ => false,
}
}
pub fn function(&self) -> Option<&FunctionIdx> {
match self {
IR::Function(ref idx) => Some(idx),
_ => None,
}
}
}
/// A constant value.

View file

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

View file

@ -56,6 +56,15 @@ impl Ty {
}
}
pub fn unknown_type(location: Location) -> Ty {
Ty {
name: None,
location,
kind: TyKind::Unresolved,
inner: TyInner::Empty,
}
}
pub fn name(&self) -> Option<StringIdx> {
self.name.clone()
}

View file

@ -0,0 +1,95 @@
use crate::ast::{Identifier, Location, Term, TypeSpec, Value};
use crate::grammar::Rule;
use crate::input_location::InputLocation;
use pest::iterators::Pair;
#[derive(Debug, Clone, PartialEq)]
pub struct Function {
clauses: Vec<Clause>,
location: InputLocation,
}
impl Value for Function {
type Item = Vec<Clause>;
fn value(self) -> Self::Item {
self.clauses
}
fn value_ref(&self) -> &Self::Item {
&self.clauses
}
}
impl Location for Function {
fn location(&self) -> &InputLocation {
&self.location
}
}
impl<'a> From<Pair<'a, Rule>> for Function {
fn from(pair: Pair<'a, Rule>) -> Self {
match pair.as_rule() {
Rule::function => {
let clauses = pair.clone().into_inner().map(|p| Clause::from(p)).collect();
let location = InputLocation::from(pair.into_span());
Function { clauses, location }
}
_ => unreachable!("Expected pair to be a Function"),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Clause {
arguments: Vec<(Identifier, TypeSpec)>,
body: Vec<Term>,
location: InputLocation,
}
impl Clause {
pub fn arguments(&self) -> &Vec<(Identifier, TypeSpec)> {
&self.arguments
}
pub fn body(&self) -> &Vec<Term> {
&self.body
}
}
impl Location for Clause {
fn location(&self) -> &InputLocation {
&self.location
}
}
impl<'a> From<Pair<'a, Rule>> for Clause {
fn from(pair: Pair<'a, Rule>) -> Self {
match pair.as_rule() {
Rule::function_clause => {
println!("clause = {:#?}", pair);
let mut inner = pair.clone().into_inner();
let arguments = inner
.next()
.unwrap()
.into_inner()
.map(|p| {
println!("p = {:?}", 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 body = inner.next().unwrap().into_inner().map(Term::from).collect();
let location = InputLocation::from(pair);
Clause {
arguments,
body,
location,
}
}
_ => unreachable!("Expected pair to be a Function Clause"),
}
}
}

View file

@ -2,6 +2,7 @@ mod atom;
pub mod binary;
mod boolean;
mod float;
mod function;
mod identifier;
mod integer;
mod local;
@ -15,6 +16,7 @@ pub use atom::Atom;
pub use binary::Binary;
pub use boolean::Boolean;
pub use float::Float;
pub use function::Function;
pub use identifier::Identifier;
pub use integer::Integer;
pub use local::Local;

View file

@ -38,6 +38,7 @@ pub enum NodeType {
Call,
Declaration,
Local,
Function,
TypeDef,
TraitDef,
@ -54,24 +55,21 @@ enum Inner {
Binary(Binary, Box<Term>, Box<Term>),
Boolean(Boolean),
Call(Identifier, Vec<Term>),
Ty(Ty),
Constructor(Ty, Vec<(Identifier, Term)>),
Declaration(Identifier, Box<Term>),
Float(Float),
Function(Function),
ImplDef(Ty, Vec<Term>),
Integer(Integer),
Local(Local),
Map(Vec<(Term, Term)>),
String(String),
TypeDef(Ty, Vec<(Identifier, TypeSpec)>, Vec<Term>),
TraitDef(Ty, Option<TypeSpec>, Vec<Term>),
ImplDef(Ty, Vec<Term>),
PublicMethod(
PrivateMethod(
Identifier,
Vec<(Identifier, TypeSpec)>,
Option<TypeSpec>,
Vec<Term>,
),
PrivateMethod(
PublicMethod(
Identifier,
Vec<(Identifier, TypeSpec)>,
Option<TypeSpec>,
@ -83,6 +81,10 @@ enum Inner {
Option<TypeSpec>,
Vec<Term>,
),
String(String),
TraitDef(Ty, Option<TypeSpec>, Vec<Term>),
Ty(Ty),
TypeDef(Ty, Vec<(Identifier, TypeSpec)>, Vec<Term>),
Unary(Unary, Box<Term>),
}
@ -124,23 +126,24 @@ impl Term {
pub fn node_type(&self) -> NodeType {
match self.inner {
Inner::Array(_) => NodeType::Array,
Inner::Atom(_) => NodeType::Atom,
Inner::Boolean(_) => NodeType::Boolean,
Inner::Binary(_, _, _) => NodeType::Binary,
Inner::Call(_, _) => NodeType::Call,
Inner::Ty(_) => NodeType::Ty,
Inner::Constructor(_, _) => NodeType::Constructor,
Inner::Declaration(_, _) => NodeType::Declaration,
Inner::Float(_) => NodeType::Float,
Inner::Integer(_) => NodeType::Integer,
Inner::Local(_) => NodeType::Local,
Inner::Map(_) => NodeType::Map,
Inner::String(_) => NodeType::String,
Inner::Unary(_, _) => NodeType::Unary,
Inner::TypeDef(_, _, _) => NodeType::TypeDef,
Inner::TraitDef(_, _, _) => NodeType::TraitDef,
Inner::ImplDef(_, _) => NodeType::ImplDef,
Inner::Array(..) => NodeType::Array,
Inner::Atom(..) => NodeType::Atom,
Inner::Boolean(..) => NodeType::Boolean,
Inner::Binary(..) => NodeType::Binary,
Inner::Call(..) => NodeType::Call,
Inner::Ty(..) => NodeType::Ty,
Inner::Constructor(..) => NodeType::Constructor,
Inner::Declaration(..) => NodeType::Declaration,
Inner::Function(..) => NodeType::Function,
Inner::Float(..) => NodeType::Float,
Inner::Integer(..) => NodeType::Integer,
Inner::Local(..) => NodeType::Local,
Inner::Map(..) => NodeType::Map,
Inner::String(..) => NodeType::String,
Inner::Unary(..) => NodeType::Unary,
Inner::TypeDef(..) => NodeType::TypeDef,
Inner::TraitDef(..) => NodeType::TraitDef,
Inner::ImplDef(..) => NodeType::ImplDef,
Inner::PublicMethod(_, _, _, _) => NodeType::PublicMethod,
Inner::PrivateMethod(_, _, _, _) => NodeType::PrivateMethod,
Inner::StaticMethod(_, _, _, _) => NodeType::StaticMethod,
@ -217,6 +220,13 @@ impl Term {
}
}
pub fn function(&self) -> Option<&Function> {
match self.inner {
Inner::Function(ref function) => Some(function),
_ => None,
}
}
pub fn public_method(
&self,
) -> Option<(
@ -402,6 +412,10 @@ impl<'a> From<Pair<'a, Rule>> for Term {
location: InputLocation::from(pair.clone()),
inner: Inner::Float(Float::from(pair)),
},
Rule::function => Term {
location: InputLocation::from(pair.clone()),
inner: Inner::Function(Function::from(pair)),
},
Rule::infix => precedence::climb(pair),
Rule::integer => Term {
location: InputLocation::from(pair.clone()),
@ -977,6 +991,33 @@ mod test {
assert_eq!(rhs2.integer().unwrap().value_ref(), &4);
}
#[test]
fn test_anonymous_function() {
let terms = Term::input(
r#"
fn (speed: Integer) do
"WAT"
end,
(speed: Float) do
"WAT"
end
"#,
)
.unwrap();
let function = terms[0].function().unwrap();
let clauses = function.value_ref();
assert_eq!(clauses.len(), 2);
let clause0 = &clauses[0];
let clause1 = &clauses[1];
assert_eq!(clause0.arguments().len(), 1);
assert_eq!(clause0.body().len(), 1);
assert_eq!(clause1.arguments().len(), 1);
assert_eq!(clause1.body().len(), 1);
}
// #[test]
// fn test_acceptance() {
// let terms = Term::input(

View file

@ -1,68 +1,73 @@
input = _{ SOI ~ expression+ ~ EOI }
file = _{ SOI ~ definition* ~ EOI }
input = _{ SOI ~ expression+ ~ EOI }
file = _{ SOI ~ definition* ~ EOI }
WHITESPACE = _{ (" " | "\t" | "\r" | "\n")+ }
newline = _{ (" " | "\t")* ~ ("\n" | "\r")+ ~ (" " | "\t")* }
reserved = { "end" | "let" | "true" | "false" }
WHITESPACE = _{ (" " | "\t" | "\r" | "\n")+ }
newline = _{ (" " | "\t")* ~ ("\n" | "\r")+ ~ (" " | "\t")* }
reserved = { "end" | "let" | "true" | "false" }
expression = _{ infix | expression_inner }
infix = { expression_inner ~ (binary_operator ~ expression_inner)+ }
expression_inner = _{ call | declaration | unary | literal | local | braced_expression }
braced_expression = _{ "(" ~ expression ~ ")" }
expression = _{ infix | expression_inner }
infix = { expression_inner ~ (binary_operator ~ expression_inner)+ }
expression_inner = _{ function | call | declaration | unary | literal | local | braced_expression }
braced_expression = _{ "(" ~ expression ~ ")" }
declaration = { "let" ~ ident ~ assign ~ expression }
declaration = { "let" ~ ident ~ assign ~ expression }
unary = { unary_operator ~ (literal | local | braced_expression) }
unary = { unary_operator ~ (literal | local | braced_expression) }
call = { ident ~ call_arguments }
call_arguments = _{ "(" ~ (call_argument ~ ("," ~ call_argument)* )? ~ ")" }
call_argument = { expression }
call = { ident ~ call_arguments }
call_arguments = _{ "(" ~ (call_argument ~ ("," ~ call_argument)* )? ~ ")" }
call_argument = { expression }
defargs = _{ "(" ~ (defarg ~ ("," ~ defarg)*)? ~ ")" }
defarg = { keyword ~ typespec }
defargs = _{ "(" ~ (defarg ~ ("," ~ defarg)*)? ~ ")" }
defarg = { keyword ~ typespec }
definition = _{ typedef | traitdef }
definition = _{ typedef | traitdef }
typedef = { "type" ~ typename ~ typeprops ~ typedefblock }
typeprops = { defargs? }
typedefblock = { ("do" ~ (methoddef | impldef)* ~ "end")? }
typedef = { "type" ~ typename ~ typeprops ~ typedefblock }
typeprops = { defargs? }
typedefblock = { ("do" ~ (methoddef | impldef)* ~ "end")? }
traitdef = { "trait" ~ typename ~ traitdefreqs ~ traitdefblock }
traitdefreqs = { (":" ~ typespec)? }
traitdefblock = { ("do" ~ methoddef* ~ "end")? }
traitdef = { "trait" ~ typename ~ traitdefreqs ~ traitdefblock }
traitdefreqs = { (":" ~ typespec)? }
traitdefblock = { ("do" ~ methoddef* ~ "end")? }
impldef = { "impl" ~ typename ~ impldefblock }
impldefblock = { ("do" ~ methoddef* ~ "end")? }
impldef = { "impl" ~ typename ~ impldefblock }
impldefblock = { ("do" ~ methoddef* ~ "end")? }
methoddef = _{ defpublic | defprivate | defstatic }
methodargs = { defargs? }
methodname = @{ ident ~ ("?")? }
methodblock = { "do" ~ expression* ~ "end" }
methodrval = { (":" ~ typespec)? }
methoddef = _{ defpublic | defprivate | defstatic }
methodargs = { defargs? }
methodname = @{ ident ~ ("?")? }
methodblock = { "do" ~ expression* ~ "end" }
methodrval = { (":" ~ typespec)? }
defpublic = { "def" ~ methodname ~ methodargs ~ methodrval ~ methodblock }
defprivate = { "defp" ~ methodname ~ methodargs ~ methodrval ~ methodblock }
defstatic = { "defs" ~ methodname ~ methodargs ~ methodrval ~ methodblock }
defpublic = { "def" ~ methodname ~ methodargs ~ methodrval ~ methodblock }
defprivate = { "defp" ~ methodname ~ methodargs ~ methodrval ~ methodblock }
defstatic = { "defs" ~ methodname ~ methodargs ~ methodrval ~ methodblock }
typespec = { typename ~ ("+" ~ typename)* }
typespec = { typename ~ ("+" ~ typename)* }
literal = _{ constructor | map | array | typename | string | atom | float | integer | boolean }
literal = _{ constructor | map | array | typename | string | atom | float | integer | boolean }
ident = @{ !reserved ~ 'a'..'z' ~ ('a'..'z' | 'A'..'Z' | "_")* }
keyword = @{ ident ~ ":" }
ident = @{ !reserved ~ 'a'..'z' ~ ('a'..'z' | 'A'..'Z' | "_")* }
keyword = @{ ident ~ ":" }
constructor = { typename ~ "{" ~ (constructor_property ~ ("," ~ constructor_property)*)? ~ "}" }
constructor_property = { keyword ~ expression }
constructor = { typename ~ "{" ~ (constructor_property ~ ("," ~ constructor_property)*)? ~ "}" }
constructor_property = { keyword ~ expression }
map = { "{" ~ (map_pair ~ ("," ~ map_pair)*)? ~ "}" }
map_pair = { (keyword | (expression ~ "=>")) ~ expression }
map = { "{" ~ (map_pair ~ ("," ~ map_pair)*)? ~ "}" }
map_pair = { (keyword | (expression ~ "=>")) ~ expression }
array = { "[" ~ (expression ~ ("," ~ expression)*)? ~ "]" }
array = { "[" ~ (expression ~ ("," ~ expression)*)? ~ "]" }
atom = @{ ":" ~ ident }
atom = @{ ":" ~ ident }
typename = @{ typename_name ~ ("." ~ typename_name)* }
typename_name = @{ 'A'..'Z' ~ ('a'..'z' | 'A'..'Z' | "_")* }
typename = @{ typename_name ~ ("." ~ typename_name)* }
typename_name = @{ 'A'..'Z' ~ ('a'..'z' | 'A'..'Z' | "_")* }
function = { "fn" ~ function_clause ~ ("," ~ function_clause)* }
function_args = { defargs }
function_clause = { function_args ~ function_block }
function_block = { "do" ~ expression* ~ "end" }
float = ${ float_characteristic ~ "." ~ float_mantissa }
float_characteristic = { "0" | (('1'..'9') ~ ('0'..'9' | "_")*) }
@ -135,6 +140,6 @@ binary_operator = _{
}
unary_operator = _{ plus | minus | logical_not }
boolean = { boolean_true | boolean_false }
boolean_true = { "true" }
boolean_false = { "false" }
boolean = { boolean_true | boolean_false }
boolean_true = { "true" }
boolean_false = { "false" }