From 7219b9c2d65160eb3d825b850ed54f24bbadec3b Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 16 Jun 2015 11:28:41 -0400 Subject: [PATCH] Type inference basically working now --- src/main.rs | 1 + src/normalize/macro_expand/test.rs | 34 +-------- src/normalize/mod.rs | 3 + src/normalize/test_util.rs | 24 +++++++ src/normalize/tyinfer/mod.rs | 92 ++++++++++++++---------- src/normalize/tyinfer/test.rs | 111 +++++++++++++++++++++++++++++ src/normalize/util.rs | 2 - src/parser/test.rs | 1 + 8 files changed, 194 insertions(+), 74 deletions(-) create mode 100644 src/normalize/test_util.rs create mode 100644 src/normalize/tyinfer/test.rs delete mode 100644 src/normalize/util.rs diff --git a/src/main.rs b/src/main.rs index 8e68673..a921956 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,6 +10,7 @@ mod intern; mod normalize; mod parser; +#[cfg(not(test))] fn main() { println!("Hello, world!"); } diff --git a/src/normalize/macro_expand/test.rs b/src/normalize/macro_expand/test.rs index 4d563f5..262480a 100644 --- a/src/normalize/macro_expand/test.rs +++ b/src/normalize/macro_expand/test.rs @@ -1,30 +1,8 @@ -use regex::Regex; use parser; -use std::fmt::Debug; +use normalize::test_util::compare; use super::expand_macros; -thread_local! { - static SPAN: Regex = - Regex::new(r"Span\([0-9 ,]*\)").unwrap() -} - -fn compare(actual: D, expected: D) { - println!("Actual:"); - println!("{:#?}", actual); - println!("Expected:"); - println!("{:#?}", expected); - - let actual = format!("{:?}", actual); - let expected = format!("{:?}", expected); - - SPAN.with(|span| { - let actual = span.replace_all(&actual, "Span(..)"); - let expected = span.replace_all(&expected, "Span(..)"); - assert_eq!(actual, expected); - }); -} - #[test] fn test_comma() { let grammar = parser::parse_grammar(" @@ -51,11 +29,6 @@ grammar Foo { } ").unwrap(); - println!("Actual:"); - println!("{:#?}", actual); - println!("Expected:"); - println!("{:#?}", expected); - compare(actual, expected); } @@ -90,10 +63,5 @@ grammar Foo { } ").unwrap(); - println!("Actual:"); - println!("{:#?}", actual); - println!("Expected:"); - println!("{:#?}", expected); - compare(actual, expected); } diff --git a/src/normalize/mod.rs b/src/normalize/mod.rs index f91c130..6e4f5a5 100644 --- a/src/normalize/mod.rs +++ b/src/normalize/mod.rs @@ -23,6 +23,9 @@ macro_rules! return_err { } } +#[cfg(test)] +mod test_util; + // These are executed *IN ORDER*: // Expands macros and expressions diff --git a/src/normalize/test_util.rs b/src/normalize/test_util.rs new file mode 100644 index 0000000..e409781 --- /dev/null +++ b/src/normalize/test_util.rs @@ -0,0 +1,24 @@ +use regex::Regex; +use std::fmt::Debug; + +thread_local! { + static SPAN: Regex = + Regex::new(r"Span\([0-9 ,]*\)").unwrap() +} + +pub fn compare(actual: D, expected: D) { + println!("Actual:"); + println!("{:#?}", actual); + println!("Expected:"); + println!("{:#?}", expected); + + let actual = format!("{:?}", actual); + let expected = format!("{:?}", expected); + + SPAN.with(|span| { + let actual = span.replace_all(&actual, "Span(..)"); + let expected = span.replace_all(&expected, "Span(..)"); + assert_eq!(actual, expected); + }); +} + diff --git a/src/normalize/tyinfer/mod.rs b/src/normalize/tyinfer/mod.rs index 75277a2..9525f17 100644 --- a/src/normalize/tyinfer/mod.rs +++ b/src/normalize/tyinfer/mod.rs @@ -5,6 +5,9 @@ use grammar::parse_tree::{Alternative, Grammar, GrammarItem, Span, Symbol, TypeRef}; use normalize::{NormResult, NormError}; +#[cfg(test)] +mod test; + pub fn infer_types(mut grammar: Grammar) -> NormResult { { let mut inferencer = try!(TypeInferencer::new(&mut grammar)); @@ -14,7 +17,6 @@ pub fn infer_types(mut grammar: Grammar) -> NormResult { } struct TypeInferencer<'a> { - grammar_span: Span, token_type: TypeRef, stack: Vec, nonterminals: HashMap>, @@ -70,8 +72,7 @@ impl<'a> TypeInferencer<'a> { }) .collect(); - Ok(TypeInferencer { grammar_span: grammar.span, - token_type: token_type, + Ok(TypeInferencer { token_type: token_type, stack: vec![], nonterminals: nonterminals }) } @@ -79,7 +80,6 @@ impl<'a> TypeInferencer<'a> { fn infer_types(&mut self) -> NormResult<()> { let ids: Vec = self.nonterminals.iter() - .filter(|&(_, nt)| nt.type_decl.is_none()) .map(|(&id, _)| id) .collect(); @@ -101,37 +101,45 @@ impl<'a> TypeInferencer<'a> { return_err!(span, "cannot infer type of `{}` because it references itself", id); } - self.stack.push(id); - - if let Some(mut type_decl) = type_decl { - try!(self.refresh_type(&mut type_decl)); - return Ok(type_decl); - } - - let mut alternative_types: Vec = - try!(alternatives.iter() - .map(|alt| self.alternative_type(alt)) - .collect()); - - // if there are no alternatives, then call it an error - if alternative_types.is_empty() { - return_err!(span, - "nonterminal `{}` has no alternatives and hence parse cannot succeed", - id); - } - - for (tyN, altN) in alternative_types[1..].iter().zip(&alternatives[1..]) { - if &alternative_types[0] != tyN { - return_err!(altN.expr.span, - "type of this alternative is `{}`, \ - but type of first alternative is `{}`", - tyN, alternative_types[0]); + self.push(id, |this| { + let type_decl = type_decl; // FIXME rustc bug requires thisx + if let Some(mut type_decl) = type_decl { + try!(this.refresh_type(&mut type_decl)); + return Ok(type_decl); } - } - self.stack.pop().unwrap(); + let mut alternative_types: Vec = + try!(alternatives.iter() + .map(|alt| this.alternative_type(alt)) + .collect()); - Ok(alternative_types.pop().unwrap()) + // if there are no alternatives, then call it an error + if alternative_types.is_empty() { + return_err!(span, + "nonterminal `{}` has no alternatives and hence parse cannot succeed", + id); + } + + for (ty, alt) in alternative_types[1..].iter().zip(&alternatives[1..]) { + if &alternative_types[0] != ty { + return_err!(alt.expr.span, + "type of this alternative is `{}`, \ + but type of first alternative is `{}`", + ty, alternative_types[0]); + } + } + + Ok(alternative_types.pop().unwrap()) + }) + } + + fn push(&mut self, id: InternedString, f: F) -> R + where F: FnOnce(&mut TypeInferencer) -> R + { + self.stack.push(id); + let r = f(self); + assert_eq!(self.stack.pop().unwrap(), id); + r } fn refresh_type(&mut self, type_ref: &mut TypeRef) -> NormResult<()> { @@ -143,11 +151,9 @@ impl<'a> TypeInferencer<'a> { } return Ok(()); } - TypeRef::Lifetime(_) => { - return Ok(()); - } + TypeRef::Lifetime(_) | TypeRef::Id(_) => { - unreachable!("should have been normalized away") + return Ok(()); } TypeRef::OfSymbol(ref symbol) => { try!(self.symbol_type(symbol)) @@ -190,7 +196,7 @@ impl<'a> TypeInferencer<'a> { .collect() }; if !chosen_symbol_types.is_empty() { - return Ok(TypeRef::Tuple(chosen_symbol_types)); + return Ok(maybe_tuple(chosen_symbol_types)); } // If they didn't choose anything with a `~`, make a tuple of everything. @@ -199,12 +205,12 @@ impl<'a> TypeInferencer<'a> { .map(|sym| self.symbol_type(sym)) .collect() }; - Ok(TypeRef::Tuple(chosen_symbol_types)) + Ok(maybe_tuple(symbol_types)) } fn symbol_type(&mut self, symbol: &Symbol) -> NormResult { match *symbol { - Symbol::Terminal(id) => Ok(self.token_type.clone()), + Symbol::Terminal(_) => Ok(self.token_type.clone()), Symbol::Nonterminal(id) => self.nonterminal_type(id), Symbol::Choose(ref s) => self.symbol_type(s), Symbol::Name(_, ref s) => self.symbol_type(s), @@ -234,3 +240,11 @@ impl<'a> NT<'a> { NT { span: data.span, type_decl: &mut data.type_decl, alternatives: &data.alternatives } } } + +fn maybe_tuple(v: Vec) -> TypeRef { + if v.len() == 1 { + v.into_iter().next().unwrap() + } else { + TypeRef::Tuple(v) + } +} diff --git a/src/normalize/tyinfer/test.rs b/src/normalize/tyinfer/test.rs new file mode 100644 index 0000000..79ebb5f --- /dev/null +++ b/src/normalize/tyinfer/test.rs @@ -0,0 +1,111 @@ +use parser; +use normalize::macro_expand::expand_macros; +use normalize::tyinfer::infer_types; +use normalize::test_util::compare; + +#[test] +fn test_pairs_and_tokens() { + let grammar = parser::parse_grammar(" +grammar Foo { + token Tok where { }; + X = Y Z; + Y: Foo = \"Hi\"; + Z = \"Ho\"; +} +").unwrap(); + + let actual = expand_macros(grammar).unwrap(); + let actual = infer_types(actual).unwrap(); + + let expected = parser::parse_grammar(" +grammar Foo { + token Tok where { }; + X: (Foo, Tok) = Y Z; + Y: Foo = \"Hi\"; + Z: Tok = \"Ho\"; +} +").unwrap(); + + compare(actual, expected); +} + +#[test] +fn test_cycle_direct() { + let grammar = parser::parse_grammar(" +grammar Foo { + token Tok where { }; + X = { + X Y; + Y; + }; + Y = \"Hi\"; +} +").unwrap(); + + let actual = expand_macros(grammar).unwrap(); + assert!(infer_types(actual).is_err()); +} + +#[test] +fn test_cycle_indirect() { + let grammar = parser::parse_grammar(" +grammar Foo { + token Tok where { }; + A = B; + B = C; + C = D; + D = A; +} +").unwrap(); + + let actual = expand_macros(grammar).unwrap(); + assert!(infer_types(actual).is_err()); +} + +#[test] +fn test_macro_expansion() { + let grammar = parser::parse_grammar(" +grammar Foo { + token Tok where { }; + Two: (X, X) = X X; + Ids = Two<\"Id\">; +} +").unwrap(); + + let actual = expand_macros(grammar).unwrap(); + let actual = infer_types(actual).unwrap(); + + let expected = parser::parse_grammar(" +grammar Foo { + token Tok where { }; + Ids: (Tok, Tok) = `Two<\"Id\">`; + `Two<\"Id\">`: (Tok, Tok) = \"Id\" \"Id\"; +} +").unwrap(); + + compare(actual, expected); +} + +#[test] +fn test_macro_expansion_infer() { + let grammar = parser::parse_grammar(" +grammar Foo { + token Tok where { }; + Two = X X; + Ids = Two<\"Id\">; +} +").unwrap(); + + let actual = expand_macros(grammar).unwrap(); + let actual = infer_types(actual).unwrap(); + + let expected = parser::parse_grammar(" +grammar Foo { + token Tok where { }; + Ids: (Tok, Tok) = `Two<\"Id\">`; + `Two<\"Id\">`: (Tok, Tok) = \"Id\" \"Id\"; +} +").unwrap(); + + compare(actual, expected); +} diff --git a/src/normalize/util.rs b/src/normalize/util.rs deleted file mode 100644 index 5e190ac..0000000 --- a/src/normalize/util.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub struct SymbolKey { -} diff --git a/src/parser/test.rs b/src/parser/test.rs index 402e110..07c26fc 100644 --- a/src/parser/test.rs +++ b/src/parser/test.rs @@ -76,6 +76,7 @@ fn map1() { } #[test] +#[allow(non_snake_case)] fn mapN() { super::parse_grammar( r#"grammar Foo { Expr = { Bar => { Baz }; X ~n:Bar => { Y }; }; }"#).unwrap();