Parser
parser源代码位置位于src/nomparser
目录下,包含了词法分析和语法分析部分。
nom
nom是一个用rust编写的parser combinator库,它不像lr分析器一样提供生成代码的功能,而是 提供一组函数,这些函数可以用来组合出各种parser。
相比于lr分析器,nom的优点是它的parser combinator非常灵活,熟练后可以快速组合出各种parser, 而且可自定义性非常的强,看起来也很直观,相比很多ir生成器的语法并没有复杂多少,但是带来了更好的 语法支持(一般的ir分析生成器的语法定义文件不会有编程语言那么好的语法支持)。
会使用nom是读懂编译器parser代码的重要前提,这里强烈推荐两个nom文档:
parser结构
parser的主要功能是使用递归下降法将pivot-lang源代码转换为ast。如果你不了解递归下降法,可以先看看这篇文章。
对于pivot lang的每一条语法规则,都会在parser里对应一个分析函数,这些分析函数可能会调用其他分析函数,最终最上层的分析函数可以将完整的源代码转换为ast。
pivot lang的完整语法规则见这里
parser最顶层的函数是parse
,它接受一个源文件输出一个AST根节点。
/// # parse
///
/// parse parses the file contents specified by source by the pivot-lang syntax,
/// and returns the node representation of the file as a [ProgramNodeWrapper].
/// the `parse` doesn't attempt to read the other files besides the provided file.
#[salsa::tracked]
pub fn parse(db: &dyn Db, source: SourceProgram) -> Result<ProgramNodeWrapper<'_>, String> {
let text = source.text(db);
let re = program(Span::new_extra(text, Default::default()));
match re {
Err(e) => Err(format!("{:?}", e)),
Ok((i, node)) => {
log::info!("parse {:?}", source.path(db));
Diagnostics((source.path(db).to_string(), i.extra.errors.borrow().clone()))
.accumulate(db);
Ok(ProgramNodeWrapper::new(db, node))
}
}
}