Skip to content

Commit

Permalink
WIP: Use thunks to break cycles on non-terminal resolution
Browse files Browse the repository at this point in the history
  • Loading branch information
Xanewok committed Nov 9, 2023
1 parent e08a097 commit 9a187af
Show file tree
Hide file tree
Showing 5 changed files with 2,178 additions and 622 deletions.
180 changes: 112 additions & 68 deletions crates/solidity/inputs/language/src/definition.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use std::cell::OnceCell;
use std::collections::BTreeSet;
use std::collections::HashMap;
use std::rc::Rc;
Expand Down Expand Up @@ -62,12 +63,8 @@ impl codegen_grammar::TriviaParserDefinition for NamedTriviaParser {
}

#[derive(Debug)]
struct NamedParser(
&'static str,
&'static str,
codegen_grammar::ParserDefinitionNode,
);
impl codegen_grammar::ParserDefinition for NamedParser {
struct NamedParserThunk(&'static str, &'static str, OnceCell<ParserDefinitionNode>);
impl codegen_grammar::ParserDefinition for NamedParserThunk {
fn name(&self) -> &'static str {
self.0
}
Expand All @@ -82,7 +79,7 @@ impl codegen_grammar::ParserDefinition for NamedParser {
}

fn node(&self) -> &codegen_grammar::ParserDefinitionNode {
&self.2
self.2.get().expect("Thunk to be resolved")
}
}

Expand Down Expand Up @@ -427,73 +424,120 @@ fn resolve_grammar_element(ident: &Identifier, ctx: &mut ResolveCtx) -> GrammarE

let (lex_ctx, elem) = ctx.items.get(ident).expect("Missing item");

let path = RESOLVE_PATH.with(|path| path.borrow().clone().join(" -> "));
// eprint!("{path}: ");
// Can't use the entry API because of the borrow checker
if let Some(resolved) = ctx.resolved.get(ident) {
eprintln!("{path}: Reusing {}.", ident);
resolved.clone()
} else {
eprintln!("{path}: Resolving {}...", ident);
RESOLVE_PATH.with(|path| path.borrow_mut().push(ident.to_string()));
// FIXME: Don't leak
let lex_ctx = lex_ctx
.as_ref()
.map(|l| l.to_string().leak() as &_)
.unwrap_or("Default");

let resolved: GrammarElement = match elem.as_ref() {
Item::Trivia { item } => (Rc::new(NamedScanner(
ident.to_string().leak(),
resolve_scanner(item.scanner.clone(), ctx),
)) as Rc<dyn codegen_grammar::ScannerDefinition>)
.into(),
Item::Fragment { item } => (Rc::new(NamedScanner(
ident.to_string().leak(),
resolve_fragment(item.clone(), ctx),
))
as Rc<dyn codegen_grammar::ScannerDefinition>)
.into(),
Item::Token { item } => (Rc::new(NamedScanner(
ident.to_string().leak(),
resolve_token(item.clone(), ctx),
)) as Rc<dyn codegen_grammar::ScannerDefinition>)
.into(),
Item::Keyword { item } => (Rc::new(NamedScanner(
// The non-terminals are mutually recursive (so will be the resolution of their definitions),
// so make sure to insert a thunk for non-terminals to resolve to break the cycle.
let inserted_thunk = match (elem.as_ref(), ctx.resolved.contains_key(ident)) {
(
Item::Struct { .. }
| Item::Enum { .. }
| Item::Repeated { .. }
| Item::Separated { .. },
false,
) => {
let thunk = Rc::new(NamedParserThunk(
ident.to_string().leak(),
resolve_keyword(item.clone()),
))
as Rc<dyn codegen_grammar::ScannerDefinition>)
.into(),
// TODO:
Item::Struct { item } => (Rc::new(NamedParser(
ident.to_string().leak(),
lex_ctx
.as_ref()
.map(|l| l.to_string().leak() as &_)
.unwrap_or("Default"),
resolve_sequence(item.clone(), ctx),
)) as Rc<dyn codegen_grammar::ParserDefinition>)
.into(),
Item::Enum { item } => (Rc::new(NamedParser(
lex_ctx,
OnceCell::new(),
));
ctx.resolved.insert(
ident.clone(),
(thunk.clone() as Rc<dyn codegen_grammar::ParserDefinition>).into(),
);
Some(thunk)
}
(Item::Precedence { .. }, false) => {
// TODO: Properly handle precedence parser
let thunk = Rc::new(NamedParserThunk(
ident.to_string().leak(),
lex_ctx
.as_ref()
.map(|l| l.to_string().leak() as &_)
.unwrap_or("Default"),
resolve_choice(item.clone(), ctx),
)) as Rc<dyn codegen_grammar::ParserDefinition>)
.into(),
Item::Repeated { .. } | Item::Separated { .. } | Item::Precedence { .. } => {
(Rc::new(NamedScanner(
"dummy",
codegen_grammar::ScannerDefinitionNode::Literal("dummy".to_string()),
)) as Rc<dyn codegen_grammar::ScannerDefinition>)
.into()
}
};
lex_ctx,
OnceCell::new(),
));
ctx.resolved.insert(
ident.clone(),
(thunk.clone() as Rc<dyn codegen_grammar::ParserDefinition>).into(),
);
Some(thunk)
}
_ => None,
};

let path = RESOLVE_PATH.with(|path| path.borrow().clone().join(" -> "));

match (inserted_thunk, ctx.resolved.get(ident)) {
// Already resolved
(None, Some(resolved)) => {
eprintln!("{path}: Reusing {}.", ident);
return resolved.clone();
}
(Some(..), None) => unreachable!("We just inserted a thunk!"),
// First time resolving a non-terminal named `ident` (since we just inserted a thunk)
// Any recursive resolution for this non-terminal will already use the thunk.
// Once we're finished, we initialize the cell with the resolved definition.
(Some(thunk), _) => {
eprintln!("{path}: Resolving non-terminal {}...", ident);

ctx.resolved.insert(ident.clone(), resolved.clone());
match elem.as_ref() {
Item::Struct { item } => {
thunk.2.set(resolve_sequence(item.clone(), ctx)).unwrap();
}
Item::Enum { item } => {
thunk.2.set(resolve_choice(item.clone(), ctx)).unwrap();
}

eprintln!("{path}: Resolved {}!", ident);
RESOLVE_PATH.with(|path| path.borrow_mut().pop());
Item::Repeated { .. } | Item::Separated { .. } | Item::Precedence { .. } => {
ctx.resolved.insert(
ident.clone(),
(Rc::new(NamedScanner(
"dummy",
codegen_grammar::ScannerDefinitionNode::Literal("dummy".to_string()),
)) as Rc<dyn codegen_grammar::ScannerDefinition>)
.into(),
);
}
_ => unreachable!("Only non-terminals can be resolved here"),
};

resolved
eprintln!("{path}: Resolved {}!", ident);
RESOLVE_PATH.with(|path| path.borrow_mut().pop());

ctx.resolved.get(ident).cloned().unwrap()
}
// First time resolving a terminal named `ident`
(None, None) => {
eprintln!("{path}: Resolving terminal {}...", ident);

let named_scanner = match elem.as_ref() {
Item::Trivia { item } => NamedScanner(
ident.to_string().leak(),
resolve_scanner(item.scanner.clone(), ctx),
),
Item::Fragment { item } => NamedScanner(
ident.to_string().leak(),
resolve_fragment(item.clone(), ctx),
),
Item::Token { item } => {
NamedScanner(ident.to_string().leak(), resolve_token(item.clone(), ctx))
}
Item::Keyword { item } => {
NamedScanner(ident.to_string().leak(), resolve_keyword(item.clone()))
}
_ => unreachable!("Only terminals can be resolved here"),
};

let resolved = GrammarElement::ScannerDefinition(Rc::new(named_scanner));
ctx.resolved.insert(ident.clone(), resolved.clone());

eprintln!("{path}: Resolved {}!", ident);
RESOLVE_PATH.with(|path| path.borrow_mut().pop());

resolved
}
}
}

Expand Down
Loading

0 comments on commit 9a187af

Please sign in to comment.