Repository navigation
Lifetime issues when parsing multi-file programs #929
|
I'm trying to write a recursive function called
Because pub fn check<'tokens, 'src: 'tokens>(
input_file: &Path,
input_file_parent: Option<&Path>,
source: &'src str,
mut modules: HashMap<PathBuf, Vec<Item<'tokens>>>,
) -> Result<HashMap<PathBuf, Vec<Item<'tokens>>>, String> {
let (maybe_tokens, lex_errors) = crate::lexer().parse(source).into_output_errors();
let parse_errors = if let Some(tokens) = maybe_tokens {
let (maybe_items, parse_errors) = crate::parser(crate::make_input)
.parse(crate::make_input((0..source.len()).into(), &tokens))
.into_output_errors();
if let Some(items) = maybe_items {
for item in &items {
if let Item::Use(item_use) = item
&& !item_use.is_self_module()
&& !item_use.is_std_module()
{
let input_file = item_use.file_path(input_file_parent);
let source =
std::fs::read_to_string(&input_file).map_err(|error| error.to_string())?;
modules = check(
&input_file,
input_file_parent,
&source,
modules,
)?;
}
}
if !modules.contains_key(input_file) {
modules.insert(input_file.to_path_buf(), items);
}
return Ok(modules);
}
parse_errors
} else {
Vec::new()
};
Err(lex_errors
.into_iter()
.map(|lex_error| lex_error.to_string())
.chain(
parse_errors
.into_iter()
.map(|parse_error| parse_error.to_string()),
)
.collect())
}This results in the following compiler errors: Both of these errors make sense to me and are really just standard Rust stuff: I can't return AST nodes referencing tokens that are local to the function, and I can't return tokens that reference the contents of the additional files read while looping through each file's AST. But I can't read all the files before lexing/parsing each one, because I only know which files need to be read by inspecting the AST of the previous files. I've tried a few approaches to solve this problem. I've tried storing the source of each file in a structure owned by the function that initially calls I even went so far as to do a massive change across my whole compiler to make #[must_use]
pub fn make_input<'a>(
eoi: SimpleSpan,
tokens: &'a [Spanned<Token<'a>>],
) -> impl BorrowInput<'a, Token = Token<'a>, Span = SimpleSpan> {
tokens.map(eoi, |(token, span)| (token, span))
}I couldn't figure out how to create an "owned" version of this, and kept getting: Anyway, I assume I was going down the wrong path by trying to make all the lexer/parser output non-borrowing anyway. Surely there's a good reason why all the Chumsky examples use tokens and nodes that borrow from their inputs. Any suggestions on how to lex and parse multi-file programs like this? I can provide more code if what I showed here doesn't give enough context. |
Replies: 1 comment 15 replies
|
Why does |
So, interestingly, neither of these errors really has anything to do with chumsky (sort of).
One of the lifetime errors you're seeing is pointing at a real problem. Take a look at the following code:
Here, you load the contents of a file into a
String,source. You then callcheck, which returnsResult<HashMap<PathBuf, Vec<Item<'src>>>, String>. Note that this type contains the'srclifetime, which only lives as long assource! Butsourcegets dro…