diff --git a/huia-compiler/src/ir/builder.rs b/huia-compiler/src/ir/builder.rs index 8bfb52a..22166c2 100644 --- a/huia-compiler/src/ir/builder.rs +++ b/huia-compiler/src/ir/builder.rs @@ -159,9 +159,9 @@ impl Builder { self.push_block(); - for (name, ty) in &arguments { - self.env_set(name, ty); - } + for (name, ty) in &arguments { + self.env_set(name, ty); + } for node in clause.body() { self.build(node.clone(), &mut context); @@ -330,26 +330,12 @@ impl Builder { let current_ty = self.peek_ty().unwrap(); let location = context.location(&node.location()); - let (ident, args, return_type, block) = node.private_method().unwrap(); + let (ident, args, block) = node.private_method().unwrap(); let method_name = context.constant_string(ident.value_ref().as_str()); - let return_type = match return_type { - Some(rt) => { - let types = rt - .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.clone()) - }) - .collect(); - context.anonymous_trait(types, location.clone()) - } - None => context.unknown_type(location.clone()), - }; + let return_type = context.unknown_type(location.clone()); - let mut method = Method::new_private(method_name, return_type, current_ty.clone()); + let mut method = Method::new_private(method_name, return_type, current_ty.clone(), location.clone()); let arguments: Vec<(StringIdx, TyIdx)> = args .iter() @@ -390,26 +376,12 @@ impl Builder { let current_ty = self.peek_ty().unwrap(); let location = context.location(&node.location()); - let (ident, args, return_type, block) = node.public_method().unwrap(); + let (ident, args, block) = node.public_method().unwrap(); let method_name = context.constant_string(ident.value_ref().as_str()); - let return_type = match return_type { - Some(rt) => { - let types = rt - .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.clone()) - }) - .collect(); - context.anonymous_trait(types, location.clone()) - } - None => context.unknown_type(location.clone()), - }; + let return_type = context.unknown_type(location.clone()); - let mut method = Method::new_public(method_name, return_type, current_ty.clone()); + let mut method = Method::new_public(method_name, return_type, current_ty.clone(), location.clone()); let arguments: Vec<(StringIdx, TyIdx)> = args .iter() @@ -446,30 +418,55 @@ impl Builder { context.define_method(method); } - NodeType::StaticMethod => { + NodeType::PublicMethodSpec => { let current_ty = self.peek_ty().unwrap(); let location = context.location(&node.location()); - - let (ident, args, return_type, block) = node.static_method().unwrap(); + let (ident, args, rval) = node.public_method_spec().unwrap(); let method_name = context.constant_string(ident.value_ref().as_str()); - let return_type = match return_type { - Some(rt) => { - let types = rt + let types = rval .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.clone()) + context.reference_type(&name, location) }) .collect(); - context.anonymous_trait(types, location.clone()) - } - None => context.unknown_type(location.clone()), - }; + let return_type = context.anonymous_trait(types, location.clone()); - let mut method = Method::new_static(method_name, return_type, current_ty.clone()); + let arguments: Vec<(StringIdx, TyIdx)> = args + .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(); + + let method = Method::new_public_spec(method_name, arguments, return_type, current_ty.clone(), location); + context.define_method(method); + } + NodeType::StaticMethod => { + let current_ty = self.peek_ty().unwrap(); + let location = context.location(&node.location()); + + let (ident, args, block) = node.static_method().unwrap(); + + let method_name = context.constant_string(ident.value_ref().as_str()); + let return_type = context.unknown_type(location.clone()); + + let mut method = Method::new_static(method_name, return_type, current_ty.clone(), location); let arguments: Vec<(StringIdx, TyIdx)> = args .iter() @@ -506,6 +503,44 @@ impl Builder { context.define_method(method); } + NodeType::StaticMethodSpec => { + let current_ty = self.peek_ty().unwrap(); + let location = context.location(&node.location()); + let (ident, args, rval) = node.public_method_spec().unwrap(); + + let method_name = context.constant_string(ident.value_ref().as_str()); + let types = rval + .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 return_type = context.anonymous_trait(types, location.clone()); + let arguments: Vec<(StringIdx, TyIdx)> = args + .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(); + + let method = Method::new_static_spec(method_name, arguments, return_type, current_ty.clone(), location); + context.define_method(method); + } NodeType::String => { let ty_idx = context.constant_string("Huia.Native.String"); let location = context.location(&node.location()); @@ -616,7 +651,11 @@ impl Builder { fn peek_block(&self) -> Option<&Block> { let len = self.blocks.len(); - self.blocks.get(len - 1) + if len > 0 { + self.blocks.get(len - 1) + } else { + None + } } fn peek_block_mut(&mut self) -> Option<&mut Block> { diff --git a/huia-compiler/src/lib.rs b/huia-compiler/src/lib.rs index 4187026..35d76cb 100644 --- a/huia-compiler/src/lib.rs +++ b/huia-compiler/src/lib.rs @@ -18,6 +18,7 @@ pub fn compile_file(path: &str) -> Context { let terms = Term::file(&contents).expect("Unable to parse file"); let mut context = Context::new(path); let mut builder = ir::builder::Builder::default(); + for term in terms { builder.build(term, &mut context); } diff --git a/huia-compiler/src/method.rs b/huia-compiler/src/method.rs index 68e1053..4684bda 100644 --- a/huia-compiler/src/method.rs +++ b/huia-compiler/src/method.rs @@ -1,14 +1,18 @@ use crate::block::BlockIdx; use crate::function::{Clause, ClauseIdx}; +use crate::location::Location; use crate::stable::StringIdx; use crate::ty::TyIdx; +use std::collections::BTreeMap; #[derive(Debug)] pub struct Method { clauses: Vec, + kind: MethodKind, + location: Location, name: StringIdx, return_type: TyIdx, - kind: MethodKind, + signature: BTreeMap, upon: TyIdx, } @@ -37,32 +41,97 @@ impl Method { } } - pub fn new_public(name: StringIdx, return_type: TyIdx, upon: TyIdx) -> Method { + pub fn new_private( + name: StringIdx, + return_type: TyIdx, + upon: TyIdx, + location: Location, + ) -> Method { Method { clauses: Vec::default(), - name, - return_type, - kind: MethodKind::Public, - upon, - } - } - - pub fn new_private(name: StringIdx, return_type: TyIdx, upon: TyIdx) -> Method { - Method { - clauses: Vec::default(), - name, - return_type, kind: MethodKind::Private, + location, + name, + return_type, + signature: BTreeMap::default(), upon, } } - pub fn new_static(name: StringIdx, return_type: TyIdx, upon: TyIdx) -> Method { + pub fn new_public( + name: StringIdx, + return_type: TyIdx, + upon: TyIdx, + location: Location, + ) -> Method { Method { clauses: Vec::default(), + kind: MethodKind::Public, + location, name, return_type, + signature: BTreeMap::default(), + upon, + } + } + + pub fn new_public_spec( + name: StringIdx, + arguments: Vec<(StringIdx, TyIdx)>, + return_type: TyIdx, + upon: TyIdx, + location: Location, + ) -> Method { + let signature = arguments.iter().fold(BTreeMap::new(), |mut args, (k, v)| { + args.insert(k.clone(), v.clone()); + args + }); + Method { + clauses: Vec::default(), + kind: MethodKind::Public, + location, + name, + return_type, + signature, + upon, + } + } + + pub fn new_static( + name: StringIdx, + return_type: TyIdx, + upon: TyIdx, + location: Location, + ) -> Method { + Method { + clauses: Vec::default(), kind: MethodKind::Static, + location, + name, + return_type, + signature: BTreeMap::default(), + upon, + } + } + + pub fn new_static_spec( + name: StringIdx, + arguments: Vec<(StringIdx, TyIdx)>, + return_type: TyIdx, + upon: TyIdx, + location: Location, + ) -> Method { + let signature = arguments.iter().fold(BTreeMap::new(), |mut args, (k, v)| { + args.insert(k.clone(), v.clone()); + args + }); + Method { + clauses: Vec::default(), + kind: MethodKind::Static, + location, + name, + return_type, + signature, upon, } } diff --git a/huia-compiler/src/stable.rs b/huia-compiler/src/stable.rs index b2d4c4f..a07224c 100644 --- a/huia-compiler/src/stable.rs +++ b/huia-compiler/src/stable.rs @@ -12,6 +12,7 @@ impl StringTable { self.0.get_or_intern(value).into() } + #[cfg(test)] pub fn is_empty(&self) -> bool { self.0.is_empty() } diff --git a/huia-parser/src/ast/term.rs b/huia-parser/src/ast/term.rs index e51717a..a1b2491 100644 --- a/huia-parser/src/ast/term.rs +++ b/huia-parser/src/ast/term.rs @@ -48,8 +48,10 @@ pub enum NodeType { TraitDef, ImplDef, PublicMethod, + PublicMethodSpec, PrivateMethod, StaticMethod, + StaticMethodSpec, } #[derive(Debug, Clone, PartialEq)] @@ -69,26 +71,13 @@ enum Inner { Local(Local), Map(Vec<(Term, Term)>), MethodCall(Box, Identifier, Vec), - PrivateMethod( - Identifier, - Vec<(Identifier, TypeSpec)>, - Option, - Vec, - ), + PrivateMethod(Identifier, Vec<(Identifier, TypeSpec)>, Vec), PropertyGet(Identifier), PropertySet(Vec<(Identifier, Term)>), - PublicMethod( - Identifier, - Vec<(Identifier, TypeSpec)>, - Option, - Vec, - ), - StaticMethod( - Identifier, - Vec<(Identifier, TypeSpec)>, - Option, - Vec, - ), + PublicMethod(Identifier, Vec<(Identifier, TypeSpec)>, Vec), + PublicMethodSpec(Identifier, Vec<(Identifier, TypeSpec)>, TypeSpec), + StaticMethod(Identifier, Vec<(Identifier, TypeSpec)>, Vec), + StaticMethodSpec(Identifier, Vec<(Identifier, TypeSpec)>, TypeSpec), String(String), TraitDef(Ty, Option, Vec), Ty(Ty), @@ -156,9 +145,11 @@ impl Term { Inner::TypeDef(..) => NodeType::TypeDef, Inner::TraitDef(..) => NodeType::TraitDef, Inner::ImplDef(..) => NodeType::ImplDef, - Inner::PublicMethod(_, _, _, _) => NodeType::PublicMethod, - Inner::PrivateMethod(_, _, _, _) => NodeType::PrivateMethod, - Inner::StaticMethod(_, _, _, _) => NodeType::StaticMethod, + Inner::PublicMethod(..) => NodeType::PublicMethod, + Inner::PublicMethodSpec(..) => NodeType::PublicMethodSpec, + Inner::PrivateMethod(..) => NodeType::PrivateMethod, + Inner::StaticMethod(..) => NodeType::StaticMethod, + Inner::StaticMethodSpec(..) => NodeType::StaticMethodSpec, } } @@ -271,16 +262,9 @@ impl Term { pub fn private_method( &self, - ) -> Option<( - &Identifier, - &Vec<(Identifier, TypeSpec)>, - Option<&TypeSpec>, - &Vec, - )> { + ) -> Option<(&Identifier, &Vec<(Identifier, TypeSpec)>, &Vec)> { 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 body) => Some((name, args, body)), _ => None, } } @@ -299,34 +283,34 @@ impl Term { } } - pub fn public_method( - &self, - ) -> Option<( - &Identifier, - &Vec<(Identifier, TypeSpec)>, - Option<&TypeSpec>, - &Vec, - )> { + pub fn public_method(&self) -> Option<(&Identifier, &Vec<(Identifier, TypeSpec)>, &Vec)> { 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 body) => Some((name, args, body)), _ => None, } } - pub fn static_method( + pub fn public_method_spec( &self, - ) -> Option<( - &Identifier, - &Vec<(Identifier, TypeSpec)>, - Option<&TypeSpec>, - &Vec, - )> { + ) -> Option<(&Identifier, &Vec<(Identifier, TypeSpec)>, &TypeSpec)> { match self.inner { - Inner::StaticMethod(ref name, ref args, ref rval, ref body) => { - Some((name, args, rval.as_ref(), body)) - } + Inner::PublicMethodSpec(ref name, ref args, ref rval) => Some((name, args, rval)), + _ => None, + } + } + + pub fn static_method(&self) -> Option<(&Identifier, &Vec<(Identifier, TypeSpec)>, &Vec)> { + match self.inner { + Inner::StaticMethod(ref name, ref args, ref body) => Some((name, args, body)), + _ => None, + } + } + + pub fn static_method_spec( + &self, + ) -> Option<(&Identifier, &Vec<(Identifier, TypeSpec)>, &TypeSpec)> { + match self.inner { + Inner::StaticMethodSpec(ref name, ref args, ref rval) => Some((name, args, rval)), _ => None, } } @@ -455,7 +439,7 @@ impl<'a> From> for Term { inner: Inner::Declaration(identifier, Box::new(value)), } } - Rule::defprivate => { + Rule::defprivatemethod => { let mut inner = pair.clone().into_inner(); let name = Identifier::from(inner.next().unwrap()); let args = inner @@ -469,17 +453,13 @@ impl<'a> From> for Term { (keyword, typespec) }) .collect(); - let rval = match inner.next().unwrap().into_inner().next() { - Some(value) => Some(TypeSpec::from(value)), - _ => None, - }; 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, block), } } - Rule::defpublic => { + Rule::defpublicmethod => { let mut inner = pair.clone().into_inner(); let name = Identifier::from(inner.next().unwrap()); let args = inner @@ -493,17 +473,33 @@ impl<'a> From> for Term { (keyword, typespec) }) .collect(); - let rval = match inner.next().unwrap().into_inner().next() { - Some(value) => Some(TypeSpec::from(value)), - _ => None, - }; 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, block), } } - Rule::defstatic => { + Rule::defpublicspec => { + let mut inner = pair.clone().into_inner(); + let name = Identifier::from(inner.next().unwrap()); + let args = inner + .next() + .unwrap() + .into_inner() + .map(|p| { + let mut inner = p.into_inner(); + let keyword = Identifier::from(inner.next().unwrap()); + let typespec = TypeSpec::from(inner.next().unwrap()); + (keyword, typespec) + }) + .collect(); + let rval = TypeSpec::from(inner.next().unwrap().into_inner().next().unwrap()); + Term { + location: InputLocation::from(pair), + inner: Inner::PublicMethodSpec(name, args, rval), + } + } + Rule::defstaticmethod => { let mut inner = pair.clone().into_inner(); let name = Identifier::from(inner.next().unwrap()); let args = inner @@ -517,14 +513,30 @@ impl<'a> From> for Term { (keyword, typespec) }) .collect(); - let rval = match inner.next().unwrap().into_inner().next() { - Some(value) => Some(TypeSpec::from(value)), - _ => None, - }; 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, block), + } + } + Rule::defstaticspec => { + let mut inner = pair.clone().into_inner(); + let name = Identifier::from(inner.next().unwrap()); + let args = inner + .next() + .unwrap() + .into_inner() + .map(|p| { + let mut inner = p.into_inner(); + let keyword = Identifier::from(inner.next().unwrap()); + let typespec = TypeSpec::from(inner.next().unwrap()); + (keyword, typespec) + }) + .collect(); + let rval = TypeSpec::from(inner.next().unwrap().into_inner().next().unwrap()); + Term { + location: InputLocation::from(pair), + inner: Inner::StaticMethodSpec(name, args, rval), } } Rule::float => Term { @@ -876,10 +888,9 @@ 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(); + let (name, args, block) = &body[0].static_method().unwrap(); assert_eq!(name.value_ref(), "new"); assert!(args.is_empty()); - assert!(rval.is_none()); assert_eq!(block.len(), 1); } diff --git a/huia-parser/src/ast/typespec.rs b/huia-parser/src/ast/typespec.rs index 112831a..1dc3c68 100644 --- a/huia-parser/src/ast/typespec.rs +++ b/huia-parser/src/ast/typespec.rs @@ -37,7 +37,10 @@ impl<'a> From> for TypeSpec { location: InputLocation::from(pair.into_span()), } } - _ => unreachable!("Expected pair to be an TypeSpec"), + _ => unreachable!( + "Expected pair to be an TypeSpec but was a {:?}", + pair.as_rule() + ), } } } diff --git a/huia-parser/src/grammar.pest b/huia-parser/src/grammar.pest index 37d737c..aa999a4 100644 --- a/huia-parser/src/grammar.pest +++ b/huia-parser/src/grammar.pest @@ -2,8 +2,11 @@ input = _{ SOI ~ expression+ ~ EOI } file = _{ SOI ~ definition* ~ EOI } WHITESPACE = _{ (" " | "\t" | "\r" | "\n")+ } +COMMENT = _{ comment_block | comment_line } +comment_block = @{ "###" ~ (!"###" ~ ANY)* ~ "###" } +comment_line = @{ "#" ~ (!("\r" | "\n") ~ ANY)* } newline = _{ (" " | "\t")* ~ ("\n" | "\r")+ ~ (" " | "\t")* } -reserved = { "end" | "let" | "true" | "false" | "else" } +reserved = { "end" | "let" | "true" | "false" | "else" | "def" | "defp" | "defs" | "do" } expression = _{ infix | expression_inner } infix = { expression_inner ~ (binary_operator ~ expression_inner)+ } @@ -45,27 +48,31 @@ typedefblock = { ("do" ~ (methoddef | impldef)* ~ "end")? } traitdef = { "trait" ~ typename ~ traitdefreqs ~ traitdefblock } traitdefreqs = { (":" ~ typespec)? } -traitdefblock = { ("do" ~ methoddef* ~ "end")? } +traitdefblock = { ("do" ~ (methoddef | methodspec)* ~ "end")? } impldef = { "impl" ~ typename ~ impldefblock } impldefblock = { ("do" ~ methoddef* ~ "end")? } -methoddef = _{ defpublic | defprivate | defstatic } +methoddef = _{ defpublicmethod | defprivatemethod | defstaticmethod } +methodspec = _{ defpublicspec | defstaticspec } methodargs = { defargs? } methodname = @{ ident ~ ("?")? } methodblockstatic = { "do" ~ expression* ~ "end" } methodblockinstance = { "do" ~ instance_espression* ~ "end" } -methodrval = { (":" ~ typespec)? } +methodrval = { ":" ~ typespec } -defpublic = { "def" ~ methodname ~ methodargs ~ methodrval ~ methodblockinstance } -defprivate = { "defp" ~ methodname ~ methodargs ~ methodrval ~ methodblockinstance } -defstatic = { "defs" ~ methodname ~ methodargs ~ methodrval ~ methodblockstatic } +defpublicmethod = { "def" ~ methodname ~ methodargs ~ methodblockinstance } +defprivatemethod = { "defp" ~ methodname ~ methodargs ~ methodblockinstance } +defstaticmethod = { "defs" ~ methodname ~ methodargs ~ methodblockstatic } + +defpublicspec = { "def" ~ methodname ~ methodargs ~ methodrval } +defstaticspec = { "defs" ~ methodname ~ methodargs ~ methodrval } typespec = { typename ~ ("+" ~ typename)* } literal = _{ constructor | map | array | typename | string | atom | float | integer | boolean } -ident = @{ !reserved ~ 'a'..'z' ~ ('a'..'z' | 'A'..'Z' | "_")* } +ident = @{ !reserved ~ LOWERCASE_LETTER ~ (LETTER | "_")* } keyword = @{ ident ~ ":" } if_expression = { "if" ~ instance_espression ~ if_positive ~ if_negative? ~ "end" } @@ -87,7 +94,7 @@ array = { "[" ~ (expression ~ ("," ~ expression)*)? ~ "]" atom = @{ ":" ~ ident } typename = @{ typename_name ~ ("." ~ typename_name)* } -typename_name = @{ 'A'..'Z' ~ ('a'..'z' | 'A'..'Z' | "_")* } +typename_name = @{ UPPERCASE_LETTER ~ (LETTER | "_")* } function = { "fn" ~ function_clause ~ ("," ~ function_clause)* } function_args = { defargs }