mirror of
https://github.com/fluencelabs/lalrpop
synced 2025-03-16 17:00:53 +00:00
Type inference basically working now
This commit is contained in:
parent
1b94c4276f
commit
7219b9c2d6
@ -10,6 +10,7 @@ mod intern;
|
||||
mod normalize;
|
||||
mod parser;
|
||||
|
||||
#[cfg(not(test))]
|
||||
fn main() {
|
||||
println!("Hello, world!");
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -23,6 +23,9 @@ macro_rules! return_err {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test_util;
|
||||
|
||||
// These are executed *IN ORDER*:
|
||||
|
||||
// Expands macros and expressions
|
||||
|
24
src/normalize/test_util.rs
Normal file
24
src/normalize/test_util.rs
Normal 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);
|
||||
});
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
111
src/normalize/tyinfer/test.rs
Normal file
111
src/normalize/tyinfer/test.rs
Normal 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);
|
||||
}
|
@ -1,2 +0,0 @@
|
||||
pub struct SymbolKey {
|
||||
}
|
@ -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();
|
||||
|
Loading…
x
Reference in New Issue
Block a user