mirror of
https://github.com/fluencelabs/lalrpop
synced 2025-03-16 17:00:53 +00:00
Merge pull request #263 from fitzgen/support-associated-types
Allow actions to return a grammar's type parameter's associated type
This commit is contained in:
commit
08bb4c98b9
11
lalrpop-test/src/associated_types.lalrpop
Normal file
11
lalrpop-test/src/associated_types.lalrpop
Normal file
@ -0,0 +1,11 @@
|
||||
use std::str::FromStr;
|
||||
use associated_types_lib::ParseCallbacks;
|
||||
|
||||
grammar<P>(callbacks: &mut P) where P: ParseCallbacks;
|
||||
|
||||
pub Term: P::Term = {
|
||||
<n:Num> => n.into(),
|
||||
"(" <t:Term> ")" => t,
|
||||
};
|
||||
|
||||
Num: P::Num = <s:r"[0-9]+"> => callbacks.number(i32::from_str(s).unwrap());
|
1019
lalrpop-test/src/associated_types.rs
Normal file
1019
lalrpop-test/src/associated_types.rs
Normal file
File diff suppressed because it is too large
Load Diff
32
lalrpop-test/src/associated_types_lib.rs
Normal file
32
lalrpop-test/src/associated_types_lib.rs
Normal file
@ -0,0 +1,32 @@
|
||||
use std::fmt::Debug;
|
||||
|
||||
pub trait ParseCallbacks: Eq + Debug {
|
||||
type Num: Eq + Debug + Into<Self::Term>;
|
||||
type Term: Eq + Debug;
|
||||
|
||||
fn number(&mut self, i32) -> Self::Num;
|
||||
}
|
||||
|
||||
#[derive(Eq, PartialEq, Debug)]
|
||||
pub struct TestParseCallbacks;
|
||||
|
||||
#[derive(Eq, PartialEq, Debug)]
|
||||
pub struct TestNum(pub i32);
|
||||
|
||||
#[derive(Eq, PartialEq, Debug)]
|
||||
pub struct TestTerm(pub TestNum);
|
||||
|
||||
impl From<TestNum> for TestTerm {
|
||||
fn from(n: TestNum) -> Self {
|
||||
TestTerm(n)
|
||||
}
|
||||
}
|
||||
|
||||
impl ParseCallbacks for TestParseCallbacks {
|
||||
type Num = TestNum;
|
||||
type Term = TestTerm;
|
||||
|
||||
fn number(&mut self, n: i32) -> Self::Num {
|
||||
TestNum(n)
|
||||
}
|
||||
}
|
@ -7,6 +7,11 @@ use lalrpop_util::{ErrorRecovery, ParseError};
|
||||
|
||||
use util::tok::Tok;
|
||||
|
||||
/// Tests that actions can return the grammar's type parameters' associated
|
||||
/// types.
|
||||
mod associated_types;
|
||||
mod associated_types_lib;
|
||||
|
||||
/// demonstration from the Greene text; one of the simplest grammars
|
||||
/// that still ensures we get parse tree correct
|
||||
mod sub;
|
||||
@ -458,3 +463,12 @@ fn issue_113() {
|
||||
fn issue_253() {
|
||||
assert!(partial_parse::parse_Term("(22))").is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_action_return_associated_types() {
|
||||
let mut callbacks = associated_types_lib::TestParseCallbacks;
|
||||
assert_eq!(
|
||||
associated_types::parse_Term(&mut callbacks, "(((((42)))))"),
|
||||
Ok(associated_types_lib::TestTerm(associated_types_lib::TestNum(42)))
|
||||
);
|
||||
}
|
||||
|
@ -61,7 +61,8 @@ mod __parse__Term {
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub enum __Nonterminal<> {
|
||||
pub enum __Nonterminal<>
|
||||
{
|
||||
Num((usize, i32, usize)),
|
||||
Term((usize, i32, usize)),
|
||||
____Term((usize, i32, usize)),
|
||||
@ -496,7 +497,8 @@ mod __parse__Term {
|
||||
use std::str::FromStr;
|
||||
extern crate lalrpop_util as __lalrpop_util;
|
||||
#[allow(dead_code)]
|
||||
pub enum __Symbol<'input> {
|
||||
pub enum __Symbol<'input>
|
||||
{
|
||||
Term_22_28_22(&'input str),
|
||||
Term_22_29_22(&'input str),
|
||||
Termr_23_22_5b0_2d9_5d_2b_22_23(&'input str),
|
||||
@ -778,7 +780,8 @@ mod __parse__Term {
|
||||
'input,
|
||||
>(
|
||||
__symbols: &mut ::std::vec::Vec<(usize,__Symbol<'input>,usize)>
|
||||
) -> (usize, &'input str, usize) {
|
||||
) -> (usize, &'input str, usize)
|
||||
{
|
||||
match __symbols.pop().unwrap() {
|
||||
(__l, __Symbol::Term_22_28_22(__v), __r) => (__l, __v, __r),
|
||||
_ => panic!("symbol type mismatch")
|
||||
@ -788,7 +791,8 @@ mod __parse__Term {
|
||||
'input,
|
||||
>(
|
||||
__symbols: &mut ::std::vec::Vec<(usize,__Symbol<'input>,usize)>
|
||||
) -> (usize, &'input str, usize) {
|
||||
) -> (usize, &'input str, usize)
|
||||
{
|
||||
match __symbols.pop().unwrap() {
|
||||
(__l, __Symbol::Term_22_29_22(__v), __r) => (__l, __v, __r),
|
||||
_ => panic!("symbol type mismatch")
|
||||
@ -798,7 +802,8 @@ mod __parse__Term {
|
||||
'input,
|
||||
>(
|
||||
__symbols: &mut ::std::vec::Vec<(usize,__Symbol<'input>,usize)>
|
||||
) -> (usize, &'input str, usize) {
|
||||
) -> (usize, &'input str, usize)
|
||||
{
|
||||
match __symbols.pop().unwrap() {
|
||||
(__l, __Symbol::Termr_23_22_5b0_2d9_5d_2b_22_23(__v), __r) => (__l, __v, __r),
|
||||
_ => panic!("symbol type mismatch")
|
||||
@ -808,7 +813,8 @@ mod __parse__Term {
|
||||
'input,
|
||||
>(
|
||||
__symbols: &mut ::std::vec::Vec<(usize,__Symbol<'input>,usize)>
|
||||
) -> (usize, i32, usize) {
|
||||
) -> (usize, i32, usize)
|
||||
{
|
||||
match __symbols.pop().unwrap() {
|
||||
(__l, __Symbol::NtNum(__v), __r) => (__l, __v, __r),
|
||||
_ => panic!("symbol type mismatch")
|
||||
@ -818,7 +824,8 @@ mod __parse__Term {
|
||||
'input,
|
||||
>(
|
||||
__symbols: &mut ::std::vec::Vec<(usize,__Symbol<'input>,usize)>
|
||||
) -> (usize, i32, usize) {
|
||||
) -> (usize, i32, usize)
|
||||
{
|
||||
match __symbols.pop().unwrap() {
|
||||
(__l, __Symbol::NtTerm(__v), __r) => (__l, __v, __r),
|
||||
_ => panic!("symbol type mismatch")
|
||||
@ -828,7 +835,8 @@ mod __parse__Term {
|
||||
'input,
|
||||
>(
|
||||
__symbols: &mut ::std::vec::Vec<(usize,__Symbol<'input>,usize)>
|
||||
) -> (usize, i32, usize) {
|
||||
) -> (usize, i32, usize)
|
||||
{
|
||||
match __symbols.pop().unwrap() {
|
||||
(__l, __Symbol::Nt____Term(__v), __r) => (__l, __v, __r),
|
||||
_ => panic!("symbol type mismatch")
|
||||
|
@ -189,7 +189,7 @@ pub struct Conversion {
|
||||
pub to: Pattern<TypeRef>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct Path {
|
||||
pub absolute: bool,
|
||||
pub ids: Vec<InternedString>,
|
||||
@ -222,7 +222,7 @@ pub enum TypeRef {
|
||||
OfSymbol(SymbolKind),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub enum WhereClause<T> {
|
||||
// 'a: 'b + 'c
|
||||
Lifetime {
|
||||
@ -255,7 +255,7 @@ impl<T> WhereClause<T> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub enum TypeBound<T> {
|
||||
// The `'a` in `T: 'a`.
|
||||
Lifetime(InternedString),
|
||||
@ -296,7 +296,7 @@ impl<T> TypeBound<T> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub enum TypeBoundParameter<T> {
|
||||
// 'a
|
||||
Lifetime(InternedString),
|
||||
|
@ -167,10 +167,14 @@ pub enum InlinedSymbol {
|
||||
Inlined(ActionFn, Vec<Symbol>),
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub enum TypeRepr {
|
||||
Tuple(Vec<TypeRepr>),
|
||||
Nominal(NominalTypeRepr),
|
||||
Associated {
|
||||
type_parameter: InternedString,
|
||||
id: InternedString,
|
||||
},
|
||||
Lifetime(InternedString),
|
||||
Ref {
|
||||
lifetime: Option<InternedString>,
|
||||
@ -218,6 +222,8 @@ impl TypeRepr {
|
||||
None => vec![],
|
||||
})
|
||||
.collect(),
|
||||
TypeRepr::Associated { type_parameter, .. } =>
|
||||
vec![TypeParameter::Id(type_parameter)],
|
||||
TypeRepr::Lifetime(l) =>
|
||||
vec![TypeParameter::Lifetime(l)],
|
||||
TypeRepr::Ref { ref lifetime, mutable: _, ref referent } =>
|
||||
@ -229,7 +235,7 @@ impl TypeRepr {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct NominalTypeRepr {
|
||||
pub path: Path,
|
||||
pub types: Vec<TypeRepr>
|
||||
@ -368,6 +374,8 @@ impl Display for TypeRepr {
|
||||
write!(fmt, "({})", Sep(", ", types)),
|
||||
TypeRepr::Nominal(ref data) =>
|
||||
write!(fmt, "{}", data),
|
||||
TypeRepr::Associated { type_parameter, id } =>
|
||||
write!(fmt, "{}::{}", type_parameter, id),
|
||||
TypeRepr::Lifetime(id) =>
|
||||
write!(fmt, "{}", id),
|
||||
TypeRepr::Ref { lifetime: None, mutable: false, ref referent } =>
|
||||
|
@ -3,6 +3,7 @@
|
||||
//! [recursive ascent]: https://en.wikipedia.org/wiki/Recursive_ascent_parser
|
||||
|
||||
use collections::{Multimap, Set};
|
||||
use grammar::parse_tree::WhereClause;
|
||||
use grammar::repr::{Grammar, NonterminalString, Production, Symbol, TerminalString, TypeParameter,
|
||||
TypeRepr};
|
||||
use lr1::core::*;
|
||||
@ -42,6 +43,8 @@ struct RecursiveAscent<'ascent, 'grammar> {
|
||||
|
||||
/// type parameters for the `Nonterminal` type
|
||||
nonterminal_type_params: Vec<TypeParameter>,
|
||||
|
||||
nonterminal_where_clauses: Vec<WhereClause<TypeRepr>>
|
||||
}
|
||||
|
||||
/// Tracks the suffix of the stack (that is, top-most elements) that any
|
||||
@ -134,6 +137,21 @@ impl<'ascent, 'grammar, W: Write> CodeGenerator<'ascent,
|
||||
.cloned()
|
||||
.collect();
|
||||
|
||||
let mut referenced_where_clauses = Set::new();
|
||||
for wc in &grammar.where_clauses {
|
||||
wc.map(|ty| {
|
||||
if ty.referenced().iter().any(|p| nonterminal_type_params.contains(p)) {
|
||||
referenced_where_clauses.insert(wc.clone());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let nonterminal_where_clauses: Vec<_> = grammar.where_clauses
|
||||
.iter()
|
||||
.filter(|wc| referenced_where_clauses.contains(wc))
|
||||
.cloned()
|
||||
.collect();
|
||||
|
||||
let state_inputs = states.iter()
|
||||
.map(|state| Self::state_input_for(state))
|
||||
.collect();
|
||||
@ -149,6 +167,7 @@ impl<'ascent, 'grammar, W: Write> CodeGenerator<'ascent,
|
||||
graph: graph,
|
||||
state_inputs: state_inputs,
|
||||
nonterminal_type_params: nonterminal_type_params,
|
||||
nonterminal_where_clauses: nonterminal_where_clauses,
|
||||
})
|
||||
}
|
||||
|
||||
@ -179,10 +198,16 @@ impl<'ascent, 'grammar, W: Write> CodeGenerator<'ascent,
|
||||
// if we are generating multiple parsers from the same file:
|
||||
rust!(self.out, "#[allow(dead_code)]");
|
||||
rust!(self.out,
|
||||
"pub enum {}Nonterminal<{}> {{",
|
||||
"pub enum {}Nonterminal<{}>",
|
||||
self.prefix,
|
||||
Sep(", ", &self.custom.nonterminal_type_params));
|
||||
|
||||
if !self.custom.nonterminal_where_clauses.is_empty() {
|
||||
rust!(self.out, " where {}", Sep(", ", &self.custom.nonterminal_where_clauses));
|
||||
}
|
||||
|
||||
rust!(self.out, " {{");
|
||||
|
||||
// make an enum with one variant per nonterminal; I considered
|
||||
// making different enums per state, but this would mean we
|
||||
// have to unwrap and rewrap as we pass up the stack, which
|
||||
|
@ -1,6 +1,7 @@
|
||||
//! A compiler from an LR(1) table to a traditional table driven parser.
|
||||
|
||||
use collections::{Map, Set};
|
||||
use grammar::parse_tree::WhereClause;
|
||||
use grammar::repr::*;
|
||||
use lr1::core::*;
|
||||
use lr1::lookahead::Token;
|
||||
@ -221,7 +222,7 @@ enum Comment<'a, T> {
|
||||
impl<'a, T: fmt::Display> fmt::Display for Comment<'a, T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
Comment::Goto(ref token, new_state) =>
|
||||
Comment::Goto(ref token, new_state) =>
|
||||
write!(f, " // on {}, goto {}", token, new_state),
|
||||
Comment::Error(ref token) =>
|
||||
write!(f, " // on {}, error", token),
|
||||
@ -235,6 +236,8 @@ struct TableDriven<'grammar> {
|
||||
/// type parameters for the `Nonterminal` type
|
||||
symbol_type_params: Vec<TypeParameter>,
|
||||
|
||||
symbol_where_clauses: Vec<WhereClause<TypeRepr>>,
|
||||
|
||||
/// a list of each nonterminal in some specific order
|
||||
all_nonterminals: Vec<NonterminalString>,
|
||||
|
||||
@ -268,6 +271,21 @@ impl<'ascent, 'grammar, W: Write> CodeGenerator<'ascent, 'grammar, W, TableDrive
|
||||
.cloned()
|
||||
.collect();
|
||||
|
||||
let mut referenced_where_clauses = Set::new();
|
||||
for wc in &grammar.where_clauses {
|
||||
wc.map(|ty| {
|
||||
if ty.referenced().iter().any(|p| symbol_type_params.contains(p)) {
|
||||
referenced_where_clauses.insert(wc.clone());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let symbol_where_clauses: Vec<_> = grammar.where_clauses
|
||||
.iter()
|
||||
.filter(|wc| referenced_where_clauses.contains(wc))
|
||||
.cloned()
|
||||
.collect();
|
||||
|
||||
// Assign each production a unique index to use as the values for reduce
|
||||
// actions in the ACTION and EOF_ACTION tables.
|
||||
let reduce_indices: Map<&'grammar Production, usize> = grammar.nonterminals
|
||||
@ -287,6 +305,7 @@ impl<'ascent, 'grammar, W: Write> CodeGenerator<'ascent, 'grammar, W, TableDrive
|
||||
action_module,
|
||||
TableDriven {
|
||||
symbol_type_params: symbol_type_params,
|
||||
symbol_where_clauses: symbol_where_clauses,
|
||||
all_nonterminals: grammar.nonterminals
|
||||
.keys()
|
||||
.cloned()
|
||||
@ -311,10 +330,16 @@ impl<'ascent, 'grammar, W: Write> CodeGenerator<'ascent, 'grammar, W, TableDrive
|
||||
// if we are generating multiple parsers from the same file:
|
||||
rust!(self.out, "#[allow(dead_code)]");
|
||||
rust!(self.out,
|
||||
"pub enum {}Symbol<{}> {{",
|
||||
"pub enum {}Symbol<{}>",
|
||||
self.prefix,
|
||||
Sep(", ", &self.custom.symbol_type_params));
|
||||
|
||||
if !self.custom.symbol_where_clauses.is_empty() {
|
||||
rust!(self.out, " where {}", Sep(", ", &self.custom.symbol_where_clauses));
|
||||
}
|
||||
|
||||
rust!(self.out, " {{");
|
||||
|
||||
// make one variant per terminal
|
||||
for &term in &self.grammar.terminals.all {
|
||||
let name = self.variant_name_for_symbol(Symbol::Terminal(term));
|
||||
@ -661,7 +686,7 @@ impl<'ascent, 'grammar, W: Write> CodeGenerator<'ascent, 'grammar, W, TableDrive
|
||||
}
|
||||
|
||||
rust!(self.out, "}}"); // else
|
||||
|
||||
|
||||
rust!(self.out, "}}"); // while let
|
||||
|
||||
self.end_parser_fn()
|
||||
@ -987,7 +1012,13 @@ impl<'ascent, 'grammar, W: Write> CodeGenerator<'ascent, 'grammar, W, TableDrive
|
||||
"{}symbols: &mut ::std::vec::Vec<{}>",
|
||||
self.prefix,
|
||||
spanned_symbol_type);
|
||||
rust!(self.out, ") -> {} {{", self.types.spanned_type(variant_ty));
|
||||
rust!(self.out, ") -> {}", self.types.spanned_type(variant_ty));
|
||||
|
||||
if !self.custom.symbol_where_clauses.is_empty() {
|
||||
rust!(self.out, " where {}", Sep(", ", &self.custom.symbol_where_clauses));
|
||||
}
|
||||
|
||||
rust!(self.out, " {{");
|
||||
|
||||
if DEBUG_PRINT {
|
||||
rust!(self.out, "println!(\"pop_{}\");", variant_name);
|
||||
|
@ -1,7 +1,7 @@
|
||||
use super::{NormResult, NormError};
|
||||
use super::norm_util::{self, AlternativeAction, Symbols};
|
||||
|
||||
use std::collections::{HashMap};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use grammar::consts::{ERROR, INPUT_LIFETIME, LOCATION};
|
||||
use grammar::parse_tree::{ActionKind, Alternative,
|
||||
Grammar,
|
||||
@ -9,9 +9,10 @@ use grammar::parse_tree::{ActionKind, Alternative,
|
||||
Path,
|
||||
Span,
|
||||
SymbolKind,
|
||||
TypeParameter,
|
||||
TypeRef};
|
||||
use grammar::repr::{NominalTypeRepr, Types, TypeRepr};
|
||||
use intern::intern;
|
||||
use intern::{intern, InternedString};
|
||||
|
||||
#[cfg(test)]
|
||||
mod test;
|
||||
@ -25,6 +26,7 @@ struct TypeInferencer<'grammar> {
|
||||
stack: Vec<NonterminalString>,
|
||||
nonterminals: HashMap<NonterminalString, NT<'grammar>>,
|
||||
types: Types,
|
||||
type_parameters: HashSet<InternedString>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
@ -48,9 +50,18 @@ impl<'grammar> TypeInferencer<'grammar> {
|
||||
})
|
||||
.collect();
|
||||
|
||||
let type_parameters = grammar.type_parameters
|
||||
.iter()
|
||||
.filter_map(|p| match *p {
|
||||
TypeParameter::Lifetime(_) => None,
|
||||
TypeParameter::Id(ty) => Some(ty),
|
||||
})
|
||||
.collect();
|
||||
|
||||
Ok(TypeInferencer { stack: vec![],
|
||||
nonterminals: nonterminals,
|
||||
types: types })
|
||||
types: types,
|
||||
type_parameters: type_parameters })
|
||||
}
|
||||
|
||||
fn make_types(grammar: &Grammar) -> Types {
|
||||
@ -212,6 +223,13 @@ impl<'grammar> TypeInferencer<'grammar> {
|
||||
Ok(TypeRepr::Tuple(types))
|
||||
}
|
||||
TypeRef::Nominal { ref path, ref types } => {
|
||||
if path.ids.len() == 2 && self.type_parameters.contains(&path.ids[0]) {
|
||||
return Ok(TypeRepr::Associated {
|
||||
type_parameter: path.ids[0],
|
||||
id: path.ids[1],
|
||||
});
|
||||
}
|
||||
|
||||
let types = try! {
|
||||
types.iter().map(|t| self.type_ref(t)).collect()
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user