Skip to content

Commit

Permalink
WIPWIP: Use thunks to break cycles
Browse files Browse the repository at this point in the history
  • Loading branch information
Xanewok committed Nov 9, 2023
1 parent a1158a2 commit 7d33628
Show file tree
Hide file tree
Showing 5 changed files with 2,223 additions and 588 deletions.
259 changes: 191 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,74 +424,200 @@ 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(
ident.to_string().leak(),
resolve_keyword(item.clone()),
))
as Rc<dyn codegen_grammar::ScannerDefinition>)
.into(),
// TODO:
Item::Struct { item } => (Rc::new(NamedParser(
// The non-terminals are mutually recursive, and 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.get(ident)) {
(
Item::Struct { .. }
| Item::Enum { .. }
| Item::Repeated { .. }
| Item::Separated { .. },
None,
) => {
let thunk = Rc::new(NamedParserThunk(
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 { .. }, None) => {
// 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,
};

ctx.resolved.insert(ident.clone(), resolved.clone());
let path = RESOLVE_PATH.with(|path| path.borrow().clone().join(" -> "));

eprintln!("{path}: Resolved {}!", ident);
RESOLVE_PATH.with(|path| path.borrow_mut().pop());
// 1. If we inserted the thunk, then we surely need to resolve those items as usual
// 2. If we did not insert the thunk, it means:
// a) we're resolving a terminal and we need to return the resolved element
// b) we're in a recursive non-terminal resolution and we need to return the thunk

resolved
// We can only return the memoized element, if it's not just a thunk (that we just inserted)
match (inserted_thunk, ctx.resolved.get(ident)) {
(None, Some(resolved)) => {
eprintln!("{path}: Reusing {}.", ident);
return resolved.clone();
}
(Some(..), None) => unreachable!("We just inserted a thunk!"),
// If we're here, it means that we need to either resolve the thunk or the terminal
// 1. If it's the inserted thunk to resolve, we fill the cell and return the thunk
// 2. If it's the terminal to resolve, we resolve it and return the resolved element
(Some(thunk), Some(..)) => {
/* First time resolving non-terminal `ident` */
eprintln!("{path}: Resolving non-terminal {}...", ident);

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

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"),
};

// ctx.resolved.insert(ident.clone(), resolved.clone());

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

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

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(
ident.to_string().leak(),
resolve_keyword(item.clone()),
))
as Rc<dyn codegen_grammar::ScannerDefinition>)
.into(),
_ => unreachable!("Only terminals can be resolved here"),
};

ctx.resolved.insert(ident.clone(), resolved.clone());

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

resolved
}
}

// // If we're here, it means that we need to either resolve the thunk or the terminal
// // 1. If it's the inserted thunk to resolve, we fill the cell and return the thunk
// // 2. If it's the terminal to resolve, we resolve it and return the resolved element
// eprintln!("{path}: Resolving {}...", ident);
// RESOLVE_PATH.with(|path| path.borrow_mut().push(ident.to_string()));

// 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(
// ident.to_string().leak(),
// resolve_keyword(item.clone()),
// )) as Rc<dyn codegen_grammar::ScannerDefinition>)
// .into(),
// // TODO:
// Item::Struct { item } => (Rc::new(NamedParserThunk(
// 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(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()
// }
// };

// ctx.resolved.insert(ident.clone(), resolved.clone());

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

// resolved
}

struct ResolveCtx<'a> {
Expand Down
Loading

0 comments on commit 7d33628

Please sign in to comment.