Type inference basically working now

This commit is contained in:
Niko Matsakis 2015-06-16 11:28:41 -04:00
parent 1b94c4276f
commit 7219b9c2d6
8 changed files with 194 additions and 74 deletions

View File

@ -10,6 +10,7 @@ mod intern;
mod normalize;
mod parser;
#[cfg(not(test))]
fn main() {
println!("Hello, world!");
}

View File

@ -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<D:Debug>(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);
}

View File

@ -23,6 +23,9 @@ macro_rules! return_err {
}
}
#[cfg(test)]
mod test_util;
// These are executed *IN ORDER*:
// Expands macros and expressions

View File

@ -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<D:Debug>(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);
});
}

View File

@ -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<Grammar> {
{
let mut inferencer = try!(TypeInferencer::new(&mut grammar));
@ -14,7 +17,6 @@ pub fn infer_types(mut grammar: Grammar) -> NormResult<Grammar> {
}
struct TypeInferencer<'a> {
grammar_span: Span,
token_type: TypeRef,
stack: Vec<InternedString>,
nonterminals: HashMap<InternedString, NT<'a>>,
@ -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<InternedString> =
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<TypeRef> =
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<TypeRef> =
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<F,R>(&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<TypeRef> {
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>) -> TypeRef {
if v.len() == 1 {
v.into_iter().next().unwrap()
} else {
TypeRef::Tuple(v)
}
}

View File

@ -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 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 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);
}

View File

@ -1,2 +0,0 @@
pub struct SymbolKey {
}

View File

@ -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();