From 6a81f8b8b6603445ba6354650fb2e1aafcea507f Mon Sep 17 00:00:00 2001 From: Liu Xiaoyi Date: Tue, 19 Nov 2019 14:30:06 +0800 Subject: [PATCH 1/3] WIP: function parser --- src/grammar/attr.rs | 50 ++++++++++++++++ src/grammar/item.rs | 72 +++++++++++++++++++++++ src/grammar/mod.rs | 5 +- src/parser/attr.rs | 33 +++++++++++ src/parser/expr.rs | 2 +- src/parser/item.rs | 139 ++++++++++++++++++++++++++++++++++++++++++++ src/parser/mod.rs | 2 + src/parser/stmt.rs | 4 +- src/parser/type.rs | 6 ++ 9 files changed, 310 insertions(+), 3 deletions(-) create mode 100644 src/grammar/attr.rs create mode 100644 src/grammar/item.rs create mode 100644 src/parser/attr.rs create mode 100644 src/parser/item.rs diff --git a/src/grammar/attr.rs b/src/grammar/attr.rs new file mode 100644 index 0000000..5ce15b7 --- /dev/null +++ b/src/grammar/attr.rs @@ -0,0 +1,50 @@ +use super::*; + +#[derive(Debug, Clone)] +pub enum AttrValue { + /** + * #[attr=literal] + */ + Literal(super::Literal), + + // TODO: support token tree (\w macros probably?) +} + +#[derive(Debug, Clone)] +pub enum AttrType<'a> { + /** + * Specify an language item + */ + Lang, + + /** + * Specify the path of a module source file + */ + Path, + + /** + * User defined items + */ + Custom(super::SimplePath<'a>), +} + +impl<'a> AttrType<'a> { + pub fn parse(p: super::SimplePath<'a>) -> Self { + if p.from_root || p.segments.len() > 1 { + // Built-in attrs only have one segments, and are not prefixed by :: + return Self::Custom(p); + } + + match p.segments.iter().next() { + Some((PathSeg::Ident("lang"), _)) => Self::Lang, + Some((PathSeg::Ident("path"), _)) => Self::Path, + _ => return Self::Custom(p), + } + } +} + +/** + * An attribute, outer or inner + */ +#[derive(Debug, Clone)] +pub struct Attr<'a>(pub AttrType<'a>, pub AttrValue); diff --git a/src/grammar/item.rs b/src/grammar/item.rs new file mode 100644 index 0000000..b3e42f7 --- /dev/null +++ b/src/grammar/item.rs @@ -0,0 +1,72 @@ +// TODO: support MacroItem + +use super::attr::Attr; +use super::SimplePath; +use super::*; +use super::r#type::Type; + +#[derive(Debug, Clone)] +pub struct Item<'a> { + pub attr: Option>, + pub value: ItemValue<'a>, +} + +#[derive(Debug, Clone)] +pub enum ItemValue<'a> { + /** + * Module scope + */ + Module(&'a str, Option>>), + // We don't have a crate system, so no extern crate + + /** + * use foo::{bar, a::b::c}; + */ + UseDecl(Vec<(SimplePath<'a>, Option<&'a str>)>), + + /** + * Function decl/impl + */ + Func(Func<'a>), + + /** + * Type alias + * + * Currently only supports solid types + * + * TODO: supports where clause in type alises + * TODO: generics + */ + TypeAlias(TypeAlias<'a>), +} + +#[derive(Debug, Clone)] +pub struct Func<'a> { + pub name: &'a str, + // TODO: supports qualifiers and ABI + + pub params: Vec<(Pat<'a>, Type<'a>)>, + pub ret: Type<'a>, + + pub generics: Vec>, + pub r#where: Vec<(Type<'a>, Vec>)>, + + pub body: Expr<'a>, // Asserts to be an block expression +} + +#[derive(Debug, Clone)] +pub struct TypeParam<'a> { + pub name: &'a str, + pub trait_bounds: Vec>, + pub default: Option>, +} + +#[derive(Debug, Clone)] +pub struct TypeAlias<'a> { + pub name: &'a str, + + pub generics: Vec>, + pub r#where: Vec<(Type<'a>, Vec>)>, + + pub value: Type<'a>, +} diff --git a/src/grammar/mod.rs b/src/grammar/mod.rs index f2aedc5..f45dc9a 100644 --- a/src/grammar/mod.rs +++ b/src/grammar/mod.rs @@ -1,6 +1,8 @@ use std::collections::HashMap; -mod r#type; +pub mod r#type; +pub mod attr; +pub mod item; pub use r#type::*; @@ -84,6 +86,7 @@ pub enum Stmt<'a> { Empty, Let(Pat<'a>, Option>), Expr(Expr<'a>), + Item(item::Item<'a>), } #[derive(Debug, Clone)] diff --git a/src/parser/attr.rs b/src/parser/attr.rs new file mode 100644 index 0000000..b6f4300 --- /dev/null +++ b/src/parser/attr.rs @@ -0,0 +1,33 @@ +use nom::{ + named, tuple, alt, map, delimited, tag +}; + +use crate::grammar::attr::*; +use super::path::simple_path; +use super::literal::literal; + +named!(pub outer_attr<&str, Attr>, mrws!(delimited!( + tag!("#["), + attr, + tag!("]") +))); + +named!(pub inner_attr<&str, Attr>, mrws!(delimited!( + tag!("#!["), + attr, + tag!("]") +))); + +named!(attr<&str, Attr>, map!( + mrws!(tuple!( + simple_path, + attr_input + )), + |(p, v)| Attr(AttrType::parse(p), v) +)); + +// TODO: support token tree +named!(attr_input<&str, AttrValue>, map!(mrws!(preceded!( + tag!("="), + literal +)), AttrValue::Literal)); diff --git a/src/parser/expr.rs b/src/parser/expr.rs index 4647009..3d73b15 100644 --- a/src/parser/expr.rs +++ b/src/parser/expr.rs @@ -153,7 +153,7 @@ named!(pub expr_with_block<&str, Expr>, alt!( named!(pub expr<&str, Expr>, alt!(expr_without_block | expr_with_block)); /* Expr \w Block*/ -named!(block_expr<&str, Expr>, map!( +named!(pub block_expr<&str, Expr>, map!( mrws!(delimited!( tag!("{"), opt!(complete!(block_expr_inner)), diff --git a/src/parser/item.rs b/src/parser/item.rs new file mode 100644 index 0000000..9f184a2 --- /dev/null +++ b/src/parser/item.rs @@ -0,0 +1,139 @@ +use nom::{ + named, alt, map, complete, opt, tag, separated_nonempty_list, +}; + +use crate::grammar::item::*; +use crate::grammar::*; +use super::attr::*; +use super::ident::*; +use super::path::*; +use super::r#type::*; +use super::pattern::*; +use super::expr::block_expr; + +named!(pub item<&str, Item>, map!( + mrws!(tuple!(complete!(opt!(outer_attr)), item_value)), + |(a, i)| Item { attr: a, value: i } +)); + +// We don't have visibility qualifiers, so we're calling VisItem item_value +named!(item_value<&str, ItemValue>, alt!( + module + | use_decl + | map!(func, ItemValue::Func) + | map!(type_alias, ItemValue::TypeAlias) +)); + +named!(module<&str, ItemValue>, map!( + mrws!(tuple!( + tag!("mod"), + ident, + opt!(mrws!(delimited!( + tag!("{"), + mrws!(many0!(item)), + tag!("}") + ))) + )), + |(_, name, inner)| ItemValue::Module(name, inner) +)); + +// TODO: support {a, b, c} and * +named!(use_decl<&str, ItemValue>, mrws!(preceded!( + tag!("use"), + map!(mrws!(tuple!( + simple_path, + opt!(mrws!(preceded!(tag!("as"), ident))) + )), |(p, alias)| ItemValue::UseDecl(vec![(p, alias)])) +))); + +named!(type_param<&str, TypeParam>, map!( + mrws!(tuple!( + ident, + opt!(mrws!(preceded!(tag!("!"), type_param_bounds))), + opt!(mrws!(preceded!(tag!("="), r#type))) + )), + |(name, tb, default)| TypeParam { + name, + trait_bounds: tb.unwrap_or_else(Vec::new), + default, + } +)); + +/** + * TODO: investigate: the ref says trait bounds can be empty, effectively yields + * where T: + * Also, reference says that we can have 0 items + */ +named!(where_clause<&str, Vec<(Type, Vec)>>, mrws!(delimited!( + tag!("where"), + mrws!(separated_nonempty_list!(tag!(","), where_clause_item)), + opt!(complete!(tag!(","))) +))); + +named!(where_clause_item<&str, (Type, Vec)>, mrws!(separated_pair!( + r#type, + tag!(":"), + type_param_bounds +))); + +named!(generics<&str, Vec>, mrws!(delimited!( + tag!("<"), + mrws!(terminated!( + mrws!(separated_list!(tag!(","), type_param)), + opt!(complete!(tag!(","))) + )), + tag!(">") +))); + +// TODO: support qualifiers? +named!(func<&str, Func>, map!( + mrws!(tuple!( + tag!("fn"), + ident, + opt!(generics), + tag!("("), + func_params, + tag!(")"), + opt!(mrws!(preceded!(tag!("->"), r#type))), + opt!(where_clause), + block_expr + )), + |(_, name, generics, _, params, _, ret, r#where, body)| Func { + name, + params, + generics: generics.unwrap_or_else(Vec::new), + r#where: r#where.unwrap_or_else(Vec::new), + ret: ret.unwrap_or(UNIT_TYPE), + body, + } +)); + +named!(func_params<&str, Vec<(Pat, Type)>>, mrws!(terminated!( + mrws!(separated_list!( + tag!(","), + mrws!(separated_pair!( + pat, + tag!(":"), + r#type + )) + )), + opt!(complete!(tag!(","))) +))); + +named!(type_alias<&str, TypeAlias>, map!( + mrws!(tuple!( + tag!("type"), + ident, + opt!(generics), + opt!(where_clause), + tag!("="), + r#type + )), + |(_, name, generics, r#where, _, value)| TypeAlias { + name, + generics: generics.unwrap_or_else(Vec::new), + r#where: r#where.unwrap_or_else(Vec::new), + value, + } +)); + diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 630f2d6..39f39c7 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -61,5 +61,7 @@ pub mod path; pub mod pattern; pub mod stmt; pub mod r#type; +pub mod attr; +pub mod item; named!(pub parse<&str, grammar::Expr>, call!(expr::expr)); diff --git a/src/parser/stmt.rs b/src/parser/stmt.rs index 7cc2587..1beeb75 100644 --- a/src/parser/stmt.rs +++ b/src/parser/stmt.rs @@ -1,11 +1,13 @@ use nom::{alt, complete, map, named, opt, tag}; use super::expr::*; +use super::item::*; use super::pattern::pat; use crate::grammar::Stmt; named!(pub stmt<&str, Stmt>, alt!( - map!(tag!("!"), |_| Stmt::Empty) + map!(tag!(";"), |_| Stmt::Empty) + | map!(item, Stmt::Item) | let_stmt | expr_stmt )); diff --git a/src/parser/type.rs b/src/parser/type.rs index 84d2a62..a6b4659 100644 --- a/src/parser/type.rs +++ b/src/parser/type.rs @@ -127,3 +127,9 @@ named!( ); named!(pub r#type<&str, Type>, call!(type_no_bound)); + +// TODO: tailing plus sign? +named!(pub type_param_bounds<&str, Vec>, mrws!(separated_list!( + tag!("+"), + type_path +))); From c5beb362c151a9a0f0211cc3de86f566133b6179 Mon Sep 17 00:00:00 2001 From: Liu Xiaoyi Date: Tue, 19 Nov 2019 16:02:32 +0800 Subject: [PATCH 2/3] Fixed method calling syntax. Added testcases for functions --- src/parser/expr.rs | 129 +++++++++++++++++++++++++-------------- src/parser/item.rs | 15 +++-- src/parser/mod.rs | 2 +- testcases/expr_chain.txt | 1 + testcases/func.txt | 5 ++ 5 files changed, 100 insertions(+), 52 deletions(-) create mode 100644 testcases/expr_chain.txt create mode 100644 testcases/func.txt diff --git a/src/parser/expr.rs b/src/parser/expr.rs index 3d73b15..a3d783b 100644 --- a/src/parser/expr.rs +++ b/src/parser/expr.rs @@ -12,6 +12,8 @@ use super::r#type::type_no_bound; use super::stmt::stmt; use crate::grammar::*; +use std::iter::Iterator; + named!(pub t1_expr<&str, Expr>, alt!( grouped_expr | map!(literal, Expr::Literal) // literal expr @@ -24,14 +26,8 @@ named!(pub t2_expr<&str, Expr>, alt!( | t1_expr )); -// T2 falls through field/tuple idx -named!(pub t3_expr<&str, Expr>, call!(t3_full)); - -// T3 falls through call/array idx -named!(pub t4_expr<&str, Expr>, call!(t4_full)); - -// T4 falls through type_cast_expr -named!(pub t5_expr<&str, Expr>, call!(error_propagation_expr)); +// T2 falls through field/tuple & call/array idx & error_propagation +named!(pub t345_expr<&str, Expr>, call!(t345_full)); named!(pub t6_expr<&str, Expr>, alt!( borrow_expr @@ -39,7 +35,7 @@ named!(pub t6_expr<&str, Expr>, alt!( | neg_expr | not_expr - | t5_expr + | t345_expr )); // T6 falls through type_cast_expr @@ -219,8 +215,46 @@ enum T3Args<'a> { Field(&'a str), } -impl<'a> T3Args<'a> { +#[derive(Debug)] +struct T345Args<'a>(T3Args<'a>, Vec>); + +impl<'a> T345Args<'a> { fn finalize(self, base: Expr<'a>) -> Expr<'a> { + if let T345Args(T3Args::Field(_), ref t4) = self { + if let Some(T45Tail::Call(_)) = t4.iter().next() { + // Merge first two + let (fname, params, rest) = match self { + T345Args(T3Args::Field(fname), t4) => { + let mut it = t4.into_iter(); + let first = it.next(); + + ( + fname, + match first { + Some(T45Tail::Call(params)) => params, + _ => unreachable!() + }, + it + ) + }, + _ => unreachable!() + }; + + return rest.fold(Expr::Call { + recv: Box::new(base), + method: Some(fname), + params, + }, |acc, c| c.standalone(acc)) + } + } + + let T345Args(t3, t4) = self; + t4.into_iter().fold(t3.standalone(base), |acc, c| c.standalone(acc)) + } +} + +impl<'a> T3Args<'a> { + fn standalone(self, base: Expr<'a>) -> Expr<'a> { match self { Self::TupleIdx(idx) => Expr::TupleIndex { owner: Box::new(base), @@ -234,69 +268,82 @@ impl<'a> T3Args<'a> { } } -pub fn t3_full(input: &str) -> IResult<&str, Expr> { - let (sliced, init) = t2_expr(input)?; +pub fn t345_full(input: &str) -> IResult<&str, Expr> { + let (sliced, init_args) = t345_first(input)?; + + let init = match init_args { + (e, tail) => tail.into_iter().fold(e, |acc, t| t.standalone(acc)) + }; use nom::fold_many0; fold_many0!( sliced, - complete!(mrws!(preceded!(tag!("."), t3_seg))), + complete!(mrws!(preceded!(tag!("."), t345_seg))), init, |acc, seg| seg.finalize(acc) ) } +named!(t345_first<&str, (Expr, Vec)>, mrws!(tuple!( + t2_expr, + many0!(complete!(t45_tail)) +))); + named!(t3_seg<&str, T3Args>, alt!( complete!(map!(tuple_idx, T3Args::TupleIdx)) | complete!(map!(ident, T3Args::Field)) )); +named!(t345_seg<&str, T345Args>, map!( + mrws!(tuple!( + t3_seg, + many0!(complete!(t45_tail)) + )), + |(t3, t4)| T345Args(t3, t4) +)); + #[derive(Debug)] -enum T4Args<'a> { +enum T45Tail<'a> { ArrayIdx(Box>), - Call(Option<&'a str>, Vec>), + Call(Vec>), + Question, } -impl<'a> T4Args<'a> { - fn finalize(self, base: Expr<'a>) -> Expr<'a> { +impl<'a> T45Tail<'a> { + fn standalone(self, base: Expr<'a>) -> Expr<'a> { match self { Self::ArrayIdx(idx) => Expr::ArrayIndex { owner: Box::new(base), idx: idx, }, - Self::Call(method, params) => Expr::Call { + Self::Call(params) => Expr::Call { recv: Box::new(base), - method, + method: None, params, }, + Self::Question => Expr::Question(Box::new(base)), } } } -fn t4_full<'a>(input: &'a str) -> IResult<&'a str, Expr<'a>> { - fall_through_expr!(input, t3_expr, t4_tail, |b, t: T4Args<'a>| t.finalize(b)) -} - -named!(t4_tail<&str, T4Args>, alt!( +named!(t45_tail<&str, T45Tail>, alt!( map!( - mrws!(tuple!( - opt!(complete!(mrws!(preceded!( - tag!("."), - ident - )))), - complete!(tag!("(")), + mrws!(delimited!( + tag!("("), call_params, - complete!(tag!(")")) + tag!(")") )), - |(method, _, params, _)| T4Args::Call(method, params) + T45Tail::Call ) | map!(mrws!(delimited!( complete!(tag!("[")), expr, complete!(tag!("]")) - )), |e| T4Args::ArrayIdx(Box::new(e))) + )), |e| T45Tail::ArrayIdx(Box::new(e))) + | + map!(complete!(tag!("?")), |_| T45Tail::Question) )); named!(array_expr<&str, Expr>, map!( @@ -505,18 +552,10 @@ named!(match_expr<&str, Expr>, map!( } )); -named!(error_propagation_expr<&str, Expr>, map!(mrws!(tuple!( - t4_expr, - opt!(complete!(tag!("?"))) -)), |(e, q)| match q { - Some(_) => Expr::Question(Box::new(e)), - None => e, -})); - named!(borrow_expr<&str, Expr>, map!(mrws!(tuple!( tag!("&"), opt!(complete!(tag!("mut"))), - t5_expr + t345_expr )), |(_, m, e)| Expr::Borrow { cont: Box::new(e), is_mut: m.is_some(), @@ -524,17 +563,17 @@ named!(borrow_expr<&str, Expr>, map!(mrws!(tuple!( named!(deref_expr<&str, Expr>, map!(mrws!(preceded!( tag!("*"), - t5_expr + t345_expr )), |e| Expr::Deref(Box::new(e)))); named!(neg_expr<&str, Expr>, map!(mrws!(preceded!( tag!("-"), - t5_expr + t345_expr )), |e| Expr::Neg(Box::new(e)))); named!(not_expr<&str, Expr>, map!(mrws!(preceded!( tag!("!"), - t5_expr + t345_expr )), |e| Expr::Not(Box::new(e)))); named!(type_cast_tail<&str, Type>, diff --git a/src/parser/item.rs b/src/parser/item.rs index 9f184a2..9811217 100644 --- a/src/parser/item.rs +++ b/src/parser/item.rs @@ -28,11 +28,14 @@ named!(module<&str, ItemValue>, map!( mrws!(tuple!( tag!("mod"), ident, - opt!(mrws!(delimited!( - tag!("{"), - mrws!(many0!(item)), - tag!("}") - ))) + alt!( + map!(mrws!(delimited!( + tag!("{"), + mrws!(many0!(item)), + tag!("}") + )), Some) + | map!(tag!(";"), |_| None) + ) )), |(_, name, inner)| ItemValue::Module(name, inner) )); @@ -49,7 +52,7 @@ named!(use_decl<&str, ItemValue>, mrws!(preceded!( named!(type_param<&str, TypeParam>, map!( mrws!(tuple!( ident, - opt!(mrws!(preceded!(tag!("!"), type_param_bounds))), + opt!(mrws!(preceded!(tag!(":"), type_param_bounds))), opt!(mrws!(preceded!(tag!("="), r#type))) )), |(name, tb, default)| TypeParam { diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 39f39c7..c62d410 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -64,4 +64,4 @@ pub mod r#type; pub mod attr; pub mod item; -named!(pub parse<&str, grammar::Expr>, call!(expr::expr)); +named!(pub parse<&str, grammar::item::Item>, call!(item::item)); diff --git a/testcases/expr_chain.txt b/testcases/expr_chain.txt new file mode 100644 index 0000000..a8d4374 --- /dev/null +++ b/testcases/expr_chain.txt @@ -0,0 +1 @@ +(owner.path.1()[1]?.bar()(dafuq).take)()?() diff --git a/testcases/func.txt b/testcases/func.txt new file mode 100644 index 0000000..15c4814 --- /dev/null +++ b/testcases/func.txt @@ -0,0 +1,5 @@ +fn wtf>(a: E, b: T) -> Box + where E: Error + Send + Sync, +{ + Box::new(e.into()) +} From 6ca77979f452216a75bfa5051fdbbcf59080dec2 Mon Sep 17 00:00:00 2001 From: Liu Xiaoyi Date: Wed, 20 Nov 2019 12:58:37 +0800 Subject: [PATCH 3/3] WIP: typer --- Cargo.lock | 39 ++++++ Cargo.toml | 2 + src/grammar/mod.rs | 61 +++++++-- src/grammar/type.rs | 235 +++++++++++++++++++++++++++++++++- src/main.rs | 2 + src/parser/expr.rs | 16 ++- src/parser/type.rs | 2 +- src/registry/mod.rs | 37 ++++++ src/typing/mod.rs | 305 ++++++++++++++++++++++++++++++++++++++++++++ testcases/func.txt | 1 + 10 files changed, 680 insertions(+), 20 deletions(-) create mode 100644 src/registry/mod.rs create mode 100644 src/typing/mod.rs diff --git a/Cargo.lock b/Cargo.lock index a8ce76d..098dcae 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -110,6 +110,26 @@ dependencies = [ "unicode-segmentation 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "im" +version = "13.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "sized-chunks 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "im-rc" +version = "13.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "sized-chunks 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -142,6 +162,8 @@ name = "meow_rust" version = "0.1.0" dependencies = [ "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "im 13.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "im-rc 13.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "nom 5.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "paw 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "structopt 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -261,6 +283,14 @@ name = "semver-parser" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "sized-chunks" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "static_assertions" version = "0.3.4" @@ -329,6 +359,11 @@ dependencies = [ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "typenum" +version = "1.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "unicode-segmentation" version = "1.6.0" @@ -387,6 +422,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "f8273f13c977665c5db7eb2b99ae520952fe5ac831ae4cd09d80c4c7042b5ed9" "checksum failure_derive 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0bc225b78e0391e4b8683440bf2e63c2deeeb2ce5189eab46e2b68c6d3725d08" "checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" +"checksum im 13.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8db49f8bc08d5cc4e2bb0f7d25a6d1db2c79bc6f7d7c86c96c657eb3d214125f" +"checksum im-rc 13.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0a0197597d095c0d11107975d3175173f810ee572c2501ff4de64f4f3f119806" "checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" "checksum lexical-core 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2304bccb228c4b020f3a4835d247df0a02a7c4686098d4167762cfbbe4c5cb14" "checksum libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)" = "1a31a0627fdf1f6a39ec0dd577e101440b7db22672c0901fe00a9a6fbb5c24e8" @@ -406,6 +443,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum ryu 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bfa8506c1de11c9c4e4c38863ccbe02a305c8188e85a05a784c9e11e1c3910c8" "checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" "checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" +"checksum sized-chunks 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f01db57d7ee89c8e053245deb77040a6cc8508311f381c88749c33d4b9b78785" "checksum static_assertions 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "7f3eb36b47e512f8f1c9e3d10c2c1965bc992bd9cdb024fa581e2194501c83d3" "checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" "checksum structopt 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "c167b61c7d4c126927f5346a4327ce20abf8a186b8041bbeb1ce49e5db49587b" @@ -414,6 +452,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum synstructure 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)" = "575be94ccb86e8da37efb894a87e2b660be299b41d8ef347f9d6d79fbe61b1ba" "checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" "checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" +"checksum typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6d2783fe2d6b8c1101136184eb41be8b1ad379e4657050b8aaff0c79ee7575f9" "checksum unicode-segmentation 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0" "checksum unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7007dbd421b92cc6e28410fe7362e2e0a2503394908f417b68ec8d1c364c4e20" "checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" diff --git a/Cargo.toml b/Cargo.toml index e3879c0..beccfb0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,3 +11,5 @@ nom = { version = "5.0.1", features = ["regexp_macros"] } paw = "1.0.0" structopt = { version = "0.3.4", features = ["paw"] } failure = "0.1.6" +im = "13.0.0" +im-rc = "13.0.0" diff --git a/src/grammar/mod.rs b/src/grammar/mod.rs index f45dc9a..82ba821 100644 --- a/src/grammar/mod.rs +++ b/src/grammar/mod.rs @@ -1,4 +1,5 @@ use std::collections::HashMap; +use std::convert::Into; pub mod r#type; pub mod attr; @@ -6,7 +7,7 @@ pub mod item; pub use r#type::*; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub enum IntSuffix { U8, U16, @@ -23,16 +24,22 @@ pub enum IntSuffix { Isize, } +#[derive(Debug, Clone, PartialEq)] +pub enum FloatSuffix { + F32, + F64, +} + #[derive(Debug, Clone)] pub enum Literal { Char(char), Int(u128, Option), - Float(f64), + Float(f64, Option), Str(String), Bool(bool), } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub enum PathSeg<'a> { Super, SelfVal, @@ -40,14 +47,14 @@ pub enum PathSeg<'a> { Ident(&'a str), // TODO: cow } -#[derive(Default, Debug, Clone)] +#[derive(Default, Debug, Clone, PartialEq)] pub struct GenericArgs<'a> { pub types: Vec>, pub bindings: HashMap<&'a str, Type<'a>>, } -#[derive(Debug, Clone)] -pub struct Path<'a, T> { +#[derive(Debug, Clone, PartialEq)] +pub struct Path<'a, T: PartialEq> { pub from_root: bool, pub segments: Vec<(PathSeg<'a>, T)>, } @@ -56,13 +63,13 @@ pub type SimplePath<'a> = Path<'a, ()>; pub type PathInExpr<'a> = Path<'a, GenericArgs<'a>>; pub type TypePath<'a> = Path<'a, TypeSegArgs<'a>>; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub struct FnTypeSpec<'a> { pub args: Vec>, pub ret: Box>, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub enum TypeSegArgs<'a> { NonFnArgs(GenericArgs<'a>), FnArgs(FnTypeSpec<'a>), @@ -114,7 +121,7 @@ pub enum Expr<'a> { Array(Vec>), ArrayFill { filler: Box>, - count: Box>, + count: Option, }, Call { recv: Box>, @@ -136,6 +143,7 @@ pub enum Expr<'a> { Closure { params: Vec<(Pat<'a>, Type<'a>)>, + ret: Option>, body: Box>, }, @@ -303,3 +311,38 @@ pub struct MatchArm<'a> { pub pats: Vec>, pub guard: Option>>, } + +enum ConstEvalResult { + U128(u128), + Invalid, +} + +pub trait ConstEval { + fn eval(&self) -> ConstEvalResult; +} + +impl Into> for ConstEvalResult { + fn into(self) -> Option { + if let ConstEvalResult::U128(u) = self { return Some(u) }; + return None; + } +} + +impl ConstEval for Literal { + fn eval(&self) -> ConstEvalResult { + match self { + &Literal::Int(i, _) => ConstEvalResult::U128(i), + _ => ConstEvalResult::Invalid, + } + } +} + + +impl<'a> ConstEval for Expr<'a> { + fn eval(&self) -> ConstEvalResult { + match self { + &Expr::Literal(ref lit) => lit.eval(), + _ => ConstEvalResult::Invalid, + } + } +} diff --git a/src/grammar/type.rs b/src/grammar/type.rs index b6efdab..b57f9b1 100644 --- a/src/grammar/type.rs +++ b/src/grammar/type.rs @@ -1,13 +1,19 @@ -use std::rc::Rc; - use super::*; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub enum SolidType<'a> { + // Primitives + Bool, + Int(IntSuffix), + Float(FloatSuffix), + Char, + Str, + + // Composite types Ident(TypePath<'a>), Tuple(Vec>), Ref(bool, Box>), - Array(Box>, Box>), + Array(Box>, Option), Slice(Box>), BareFunc(FnTypeSpec<'a>), @@ -15,17 +21,234 @@ pub enum SolidType<'a> { Never, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub enum Type<'a> { Solid(SolidType<'a>), - SameAs(Rc>), Placeholder, } +#[derive(Debug, Clone, PartialEq)] +enum TypeOrd { + + /** + * Two types diverge + * + * e.g.: u16 and f32 + */ + Diverge, + /** + * Two types doesn't diverge, but has no clearing ordering between them. + * Merge of the two types yield a type that is greater than both of them + * + * e.g.: (a, _) and (_, b) + */ + NoOrd, + + /** + * Two type is equvalent in specificity + */ + Eq, + + /** + * The latter is more specific + */ + Less, + + /** + * The former is more specific + */ + Greater, +} + +impl TypeOrd { + fn from_eq(eq: bool) -> TypeOrd { + if eq { TypeOrd::Eq } else { TypeOrd::Diverge } + } + + fn join(self, ano: TypeOrd) -> TypeOrd { + match (self, ano) { + (TypeOrd::Diverge, _) => TypeOrd::Diverge, + (_, TypeOrd::Diverge) => TypeOrd::Diverge, + + (TypeOrd::NoOrd, _) => TypeOrd::NoOrd, + (_, TypeOrd::NoOrd) => TypeOrd::NoOrd, + + (TypeOrd::Eq, ret) => ret, + (ret, TypeOrd::Eq) => ret, + + (s, a) => { + if s == a { + s + } else { + TypeOrd::NoOrd + } + }, + } + } +} + impl<'a> From> for Type<'a> { fn from(f: SolidType<'a>) -> Self { Self::Solid(f) } } +impl<'a> Type<'a> { + fn merge_cmp(&self, another: &'a Type<'a>) -> (Option>, TypeOrd) { + match (self, another) { + (&Type::Placeholder, _) => { + let ord = if another == &Type::Placeholder { TypeOrd::Eq } else { TypeOrd::Less }; + (Some(another.clone()), ord) + }, + (_, &Type::Placeholder) => (Some(self.clone()), TypeOrd::Greater), + (&Type::Solid(ref s), &Type::Solid(ref a)) => { + let (s, o) = s.merge_cmp(a); + (s.map(Into::into), o) + }, + } + } +} + +impl<'a> SolidType<'a> { + fn merge_cmp(&self, another: &'a SolidType<'a>) -> (Option>, TypeOrd) { + let ret = match self { + | &SolidType::Bool + | &SolidType::Int(_) + | &SolidType::Float(_) + | &SolidType::Char + | &SolidType::Str + | &SolidType::Never + => (Some(self.clone().into()), TypeOrd::from_eq(another == self)), + + &SolidType::Ident(ref p) => { + if let &SolidType::Ident(ref anop) = another { + // TODO: conanicalize path + // TODO: generic + (Some(self.clone().into()), TypeOrd::from_eq(p == anop)) + } else { + (None, TypeOrd::Diverge) + } + }, + &SolidType::Tuple(ref elem) => { + if let &SolidType::Tuple(ref anoelem) = another { + if elem.len() != anoelem.len() { + (None, TypeOrd::Diverge) + } else { + let mut ordacc = TypeOrd::Eq; + let mut typacc = Vec::with_capacity(elem.len()); + + for (e, ae) in elem.iter().zip(anoelem.iter()) { + let (t, o) = e.merge_cmp(ae); + ordacc = ordacc.join(o); + if ordacc != TypeOrd::Diverge { + typacc.push(t.unwrap()); + } + } + + (Some(SolidType::Tuple(typacc).into()), ordacc) + } + } else { + (None, TypeOrd::Diverge) + } + }, + &SolidType::Ref(ref is_mut, ref deref) => { + if let &SolidType::Ref(ref ano_mut, ref ano_deref) = another { + if is_mut != ano_mut { + (None, TypeOrd::Diverge) + } else { + use std::ops::Deref; + deref.merge_cmp(ano_deref.deref()) + } + } else { + (None, TypeOrd::Diverge) + } + }, + + &SolidType::Array(ref base, ref count) => { + if let &SolidType::Array(ref ano_base, ref ano_count) = another { + use std::ops::Deref; + let (bt, bo) = base.merge_cmp(ano_base.deref()); + + if bo == TypeOrd::Diverge { return (None, TypeOrd::Diverge) } + let bti = bt.unwrap(); + + let (ct, co) = match (count.clone(), ano_count.clone()) { + (None, None) => (None, TypeOrd::Eq), + (Some(c), None) => (Some(c), TypeOrd::Greater), + (None, Some(c)) => (Some(c), TypeOrd::Less), + (Some(ac), Some(bc)) if ac == bc => (Some(ac), TypeOrd::Eq), + _ => (None, TypeOrd::Diverge), + }; + + (Some(SolidType::Array(Box::new(bti), ct).into()), bo.join(co)) + } else { + (None, TypeOrd::Diverge) + } + }, + + &SolidType::Slice(ref base) => { + if let &SolidType::Slice(ref ano_base) = another { + use std::ops::Deref; + base.merge_cmp(ano_base.deref()) + } else { + (None, TypeOrd::Diverge) + } + }, + + &SolidType::BareFunc(FnTypeSpec{ ref ret, ref args }) => { + if let &SolidType::BareFunc(FnTypeSpec{ ret: ref ano_ret, args: ref ano_args }) = another { + if args.len() != ano_args.len() { + (None, TypeOrd::Diverge) + } else { + use std::ops::Deref; + let (rt, ro) = ret.merge_cmp(ano_ret.deref()); + + if ro == TypeOrd::Diverge { + return (None, TypeOrd::Diverge); + } + + let rti = rt.unwrap(); + let mut ordacc = ro; + let mut typacc = Vec::with_capacity(args.len()); + + for (e, ae) in args.iter().zip(ano_args.iter()) { + let (t, o) = e.merge_cmp(ae); + ordacc = ordacc.join(o); + if ordacc != TypeOrd::Diverge { + typacc.push(t.unwrap()); + } + } + + let t = SolidType::BareFunc(FnTypeSpec { + ret: Box::new(rti), + args: typacc, + }); + (Some(t.into()), ordacc) + } + } else { + (None, TypeOrd::Diverge) + } + }, + }; + + if ret.1 == TypeOrd::Diverge { + (None, TypeOrd::Diverge) + } else { + ret + } + } + + fn is_finished(&self) -> bool { + match self { + | &SolidType::Bool + | &SolidType::Int(_) + | &SolidType::Float(_) + | &SolidType::Char + | &SolidType::Str + | &SolidType::Never + => true, + } + } +} + pub const UNIT_TYPE: Type = Type::Solid(SolidType::Tuple(Vec::new())); diff --git a/src/main.rs b/src/main.rs index 0276757..d15d3af 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,7 @@ mod grammar; mod parser; +mod typing; +mod registry; use failure::Error; use std::fs::File; diff --git a/src/parser/expr.rs b/src/parser/expr.rs index a3d783b..580d228 100644 --- a/src/parser/expr.rs +++ b/src/parser/expr.rs @@ -372,7 +372,7 @@ named!(array_expr_inner<&str, Expr>, alt!( tag!(";"), expr )), - |(f, c)| Expr::ArrayFill { filler: Box::new(f), count: Box::new(c) } + |(f, c)| Expr::ArrayFill { filler: Box::new(f), count: c.eval().into() } ) )); @@ -381,11 +381,19 @@ named!(closure_expr<&str, Expr>, map!( tag!("|"), opt!(complete!(closure_params)), tag!("|"), - expr + alt!( + map!( + mrws!(tuple!(tag!("->"), type_no_bound, block_expr)), + |(_, t, e)| (Some(t), e) + ) + | + map!(expr, |e| (None, e)) + ) )), - |(_, p, _, b)| Expr::Closure { + |(_, p, _, (r, e))| Expr::Closure { params: p.unwrap_or_else(Vec::new), - body: Box::new(b), + ret: r, + body: Box::new(e), } )); diff --git a/src/parser/type.rs b/src/parser/type.rs index a6b4659..ce529eb 100644 --- a/src/parser/type.rs +++ b/src/parser/type.rs @@ -61,7 +61,7 @@ named!(array_type<&str, Type>, expr, tag!("]") )), - |(_, t, _, n, _)| SolidType::Array(Box::new(t), Box::new(n)).into() + |(_, t, _, n, _)| SolidType::Array(Box::new(t), n.eval().into()).into() ) ); diff --git a/src/registry/mod.rs b/src/registry/mod.rs new file mode 100644 index 0000000..8c4acb1 --- /dev/null +++ b/src/registry/mod.rs @@ -0,0 +1,37 @@ +use im_rc::hashmap::HashMap; +use crate::grammar::item::*; +use failure::Error; +use failure::format_err; + +#[derive(Clone)] +pub struct RegistryNode<'a> { + item: &'a Item<'a>, + children: HashMap<&'a str, RegistryNode<'a>>, +} + +impl<'a> RegistryNode<'a> { + fn create<'f>(item: &'a Item<'a>) -> Result<(&'a str, Self), Error> { + let (name, children) = match item.value { + ItemValue::Module(name, ref items) => { + let mut children = HashMap::new(); + if let Some(inner) = items { + for item in inner.iter() { + let (cname, cnode) = RegistryNode::create(item)?; + children.insert(cname, cnode); + } + } + (name, children) + }, + ItemValue::Func(ref f) => (f.name, HashMap::new()), + ItemValue::TypeAlias(ref t) => (t.name, HashMap::new()), + // TODO: supports use decl + + _ => return Err(format_err!("Item not supported")), + }; + + Ok((name, RegistryNode::<'a> { + item, + children, + })) + } +} diff --git a/src/typing/mod.rs b/src/typing/mod.rs new file mode 100644 index 0000000..aa9f3b8 --- /dev/null +++ b/src/typing/mod.rs @@ -0,0 +1,305 @@ +use im_rc::hashmap::HashMap; +use im_rc::hashset::HashSet; +use std::collections::VecDeque; + +use crate::grammar::*; +use crate::grammar::item::*; +use crate::registry::*; + +type TypableWalkThrough<'a> = (Type<'a>, Vec<&'a dyn Typable<'a>>); + +trait Typable<'a> { + fn walk_through(&'a self, ctx: TypingCtx<'a>, typer: &mut Typer<'a>) -> TypableWalkThrough<'a>; + fn update(&'a self, hint: Type<'a>, ctx: TypingCtx<'a>, typer: &mut Typer<'a>) -> Type<'a>; + // fn probe(&self) -> Vec<&'a dyn Typable<'a>>; +} + +// Scoped variable/functions/... +#[derive(Clone)] +pub enum Scoped<'a> { + Binding { + is_mut: bool, + from: &'a Pat<'a>, + }, +} + +#[derive(Default, Clone)] +struct TypingCtx<'a> { + labels: HashMap<&'a str, &'a Expr<'a>>, + func: Option<&'a Func<'a>>, + last_loop: Option<&'a Expr<'a>>, + + reg: HashMap<&'a str, RegistryNode<'a>>, + scope: HashMap<&'a str, Scoped<'a>> +} + +type TypableId<'a> = *const (dyn Typable<'a> + 'a); + +struct Typer<'a> { + resolved: HashMap, Type<'a>>, + ctxs: HashMap, TypingCtx<'a>>, + + notify_map: HashMap, HashSet<&'a Typable<'a>>>, + + queue: VecDeque>, + nontrivial_init: HashSet>, +} + +impl<'a> Typer<'a> { + pub fn new() -> Self { + Typer::<'a> { + resolved: HashMap::new(), + ctxs: HashMap::new(), + notify_map: HashMap::new(), + queue: VecDeque::new(), + nontrivial_init: HashSet::new(), + } + } + + pub fn walk_through_child(&mut self, root: &'a Typable<'a>, ctx: TypingCtx<'a>) { + self.ctxs.insert(root, ctx.clone()); + + let (init_type, notifiers) = root.walk_through(ctx, self); + + if init_type != Type::Placeholder { + self.nontrivial_init.insert(root); + } + + self.resolved.insert(root, init_type); + } + + pub fn update_hint(&mut self, target: &'a mut Typable<'a>, hint: Type<'a>) { + } +} + +impl<'a> Typable<'a> for Literal { + fn resolve(&'a self, hint: Option>, _: TypingCtx<'a>, typer: &mut Typer<'a>) -> TypingResult<'a> { + let t = match self { + &Literal::Bool(_) => SolidType::Bool.into(), + &Literal::Char(_) => SolidType::Char.into(), + &Literal::Str(_) => SolidType::Ref(false, Box::new(SolidType::Str.into())).into(), + &Literal::Int(_, ref suf) => SolidType::Int(suf.clone().unwrap_or(IntSuffix::Isize)).into(), + &Literal::Float(_, ref suf) => SolidType::Float(suf.clone().unwrap_or(FloatSuffix::F64)).into(), + }; + + if let Some(h) = hint { + if h != t { + return TypingResult::Conflict; + } + } + + TypingResult::Success(t) + } +} + +impl<'a> Typable<'a> for Expr<'a> { + fn resolve(&'a self, hint: Option>, ctx: TypingCtx<'a>, typer: &mut Typer<'a>) -> TypingResult<'a> { + match self { + &Expr::Literal(ref lit) => typer.resolve(lit, hint, ctx), + &Expr::Tuple(ref elem) => { + let mut changed = false; + let mut sufficient = false; + let mut result = Vec::with_capacity(elem.len()); + + let h = match hint { + // TODO: use infinity iterator + None => vec![None; elem.len()], + Some(Type::Solid(SolidType::Tuple(t))) => t.into_iter().map(Some).collect(), + _ => return TypingResult::Conflict, + }; + + for (e, eh) in elem.iter().zip(h.into_iter()) { + if let Some(s) = typing_try!(typer.resolve(e, eh, ctx.clone()), changed, sufficient) { + result.push(s); + } + } + + if result.len() == elem.len() { + assert!(sufficient); + TypingResult::Success(SolidType::Tuple(result).into()) + } else { + if changed { + TypingResult::Insufficient + } else { + TypingResult::Unchanged + } + } + }, + &Expr::Array(ref elem) => { + let mut changed = false; + let mut sufficient = false; + + let mut bt = match hint { + // TODO: use infinity iterator + None => None, + // TODO: check for expr not able to be evaluated at compile-time + // Both result in an len=None, but we should emit an error if it cannot be evaluated + Some(Type::Solid(SolidType::Array(t, len))) if len.is_none() || len == Some(elem.len() as u128) + => Some(*t), + _ => return TypingResult::Conflict, + }; + + for e in elem.iter() { + if let Some(nbt) = typing_try!(typer.resolve(e, bt.clone(), ctx.clone()), changed, sufficient) { + bt = Some(nbt); + } + } + + if let Some(b) = bt { + if sufficient { + // We may actually get a sufficient=true and bt = None if we have no elment, hence the check + return TypingResult::Success(SolidType::Array(Box::new(b), Some(elem.len() as u128)).into()); + } + } + + if changed { + TypingResult::Insufficient + } else { + TypingResult::Unchanged + } + }, + &Expr::ArrayFill{ ref filler, ref count } => { + let c = if let Some(i) = count { + *i + } else { + return TypingResult::Conflict // Cannot evaluate at compile time + }; + + let bt = match hint { + // TODO: use infinity iterator + None => None, + Some(Type::Solid(SolidType::Array(t, len))) if len.is_none() || len == Some(c) + => Some(*t), + _ => return TypingResult::Conflict, + }; + + typer.resolve(filler.as_ref(), bt, ctx) + }, + &Expr::Call{ .. } => unimplemented!(), + &Expr::Field{ .. } => unimplemented!(), + &Expr::ArrayIndex{ ref owner, .. } => { + let mut changed = false; + let mut _sufficient = false; + + let owner_type = typing_try!(typer.resolve(owner.as_ref(), None, ctx), changed, _sufficient); + + if let Some(owner_inner) = owner_type { + if let Type::Solid(SolidType::Array(bt, _)) = owner_inner { + // TODO: check ascriptions + return TypingResult::Success(*bt); + } else { + unimplemented!("Desuger to ops overload calls"); + } + } + + if changed { + TypingResult::Insufficient + } else { + TypingResult::Unchanged + } + }, + &Expr::TupleIndex{ ref owner, idx } => { + let mut changed = false; + let mut _sufficient = false; + + let owner_type = typing_try!(typer.resolve(owner.as_ref(), None, ctx), changed, _sufficient); + + if let Some(owner_inner) = owner_type { + if let Type::Solid(SolidType::Tuple(tt)) = owner_inner { + // TODO: check ascriptions + return TypingResult::Success(tt[idx as usize].clone()); + } else { + return TypingResult::Conflict; + } + } + + if changed { + TypingResult::Insufficient + } else { + TypingResult::Unchanged + } + }, + &Expr::Closure{ ref params, ref ret, ref body } => unimplemented!(), + &Expr::FlowCtrl(ref fc) => match fc { + &FlowCtrl::Break() => { + }, + }, + &Expr::Block{ ref body, ref ret } => { + let mut changed = false; + let mut sufficient = false; + + let mut bctx = ctx; + + for stmt in body.iter() { + match stmt { + &Stmt::Empty => continue, + &Stmt::Expr(ref e) => { typing_try!(typer.resolve(e, None, bctx.clone()), changed, sufficient); }, + &Stmt::Let(ref p, ref e) => { + if let &Pat::Ident { ref name, ref is_ref, ref is_mut, .. } = p { + let binding_mut = *is_mut && !*is_ref; + bctx.scope.insert(name, Scoped::Binding { from: p, is_mut: binding_mut }); + } + + let pt = typing_try!(typer.resolve(p, None, bctx.clone()), changed, sufficient); + + if let Some(ei) = e { + let et = typing_try!(typer.resolve(ei, None, bctx.clone()), changed, sufficient); + + // Try again with other's type + if pt.is_some() { + typing_try!(typer.resolve(ei, pt, bctx.clone()), changed, sufficient); + } + if et.is_some() { + typing_try!(typer.resolve(p, et, bctx.clone()), changed, sufficient); + } + } + }, + &Stmt::Item(_) => unimplemented!(), + } + } + + let self_type = if let &Some(ref e) = ret { + typing_try!(typer.resolve(e.as_ref(), hint, bctx), changed, sufficient) + } else { + if hint != Some(UNIT_TYPE) { + changed = true; + } + + Some(UNIT_TYPE) + }; + + // FIXME: check hint + + if !sufficient { + if !changed { + return TypingResult::Unchanged; + } else { + return TypingResult::Insufficient; + } + } else if let Some(t) = self_type { + return TypingResult::Success(t); + } else { + return TypingResult::Insufficient; + } + }, + } + } +} + +impl<'a> Typable<'a> for Pat<'a> { + fn resolve(&'a self, hint: Option>, ctx: TypingCtx<'a>, typer: &mut Typer<'a>) -> TypingResult<'a> { + match self { + &Pat::Ident { bounded: None, is_mut, is_ref, name } => { + // TODO: type ascriptions + match hint { + Some(t) => TypingResult::Success(t), + None => TypingResult::Insufficient, + } + }, + _ => { + // Not universally typable + return TypingResult::Conflict; + } + } + } +} diff --git a/testcases/func.txt b/testcases/func.txt index 15c4814..39bca83 100644 --- a/testcases/func.txt +++ b/testcases/func.txt @@ -1,5 +1,6 @@ fn wtf>(a: E, b: T) -> Box where E: Error + Send + Sync, { + use std::func; Box::new(e.into()) }