Skip to content

Commit

Permalink
🚧 (parser): Work on earley parser
Browse files Browse the repository at this point in the history
semver: chore
  • Loading branch information
Somfic committed Jun 19, 2024
1 parent b266d67 commit 32635a8
Show file tree
Hide file tree
Showing 4 changed files with 137 additions and 44 deletions.
80 changes: 80 additions & 0 deletions src/ast/builder.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
use crate::{
ast::Statement,
diagnostic::{Diagnostic, Error},
parser::{grammar::NonTerminal, ParseNode},
};

use super::Ast;

pub fn build_ast<'a>(parse_tree: &'a ParseNode<'a>) -> Result<Ast<'a>, Vec<Diagnostic<'a>>> {
let mut diagnostics = Vec::new();
let mut ast = Ast::Statement(Statement::Empty);

match parse_tree {
ParseNode::NonTerminal(NonTerminal::Start, children) => {
match children
.iter()
.map(|child| build_ast(child))
.collect::<Result<Vec<_>, _>>()
{
Ok(_) => {}
Err(err) => {
diagnostics.extend(err);
}
}

ast = Ast::Statement(Statement::Empty);
}
_ => {
let range = parse_tree.range();

diagnostics.push(
Diagnostic::error("Structure error").with_error(Error::primary(
range.file_id,
range.position,
range.length,
format!("Expected start, got {:?}", parse_tree),
)),
);
}
}

if !diagnostics.is_empty() {
Err(diagnostics)
} else {
Ok(ast)
}
}

pub fn build_top_level_statement<'a>(
parse_tree: &'a ParseNode<'a>,
) -> Result<Ast<'a>, Vec<Diagnostic<'a>>> {
match parse_tree {
ParseNode::NonTerminal(NonTerminal::RootItems, children) => {
let root_items = children
.iter()
.map(|child| build_ast(child))
.collect::<Result<Vec<_>, _>>()?;

Ok(Ast::Statement(Statement::Empty))
}
_ => {
let range = parse_tree.range();

return Err(vec![Diagnostic::error("Structure error").with_error(
Error::primary(
range.file_id,
range.position,
range.length,
"Expected root items",
),
)]);
}
}
}

#[cfg(test)]
mod test {
#[test]
fn test() {}
}
22 changes: 22 additions & 0 deletions src/ast/mod.rs
Original file line number Diff line number Diff line change
@@ -1 +1,23 @@
use std::collections::HashSet;

use crate::diagnostic::Range;

pub mod builder;

pub enum Ast<'a> {
Statement(Statement<'a>),
}
pub enum Statement<'a> {
Empty,
EnumDeclaration(Spanned<'a, EnumDeclaration<'a>>),
}

pub struct EnumDeclaration<'a> {
pub identifier: Spanned<'a, &'a str>,
pub items: Vec<Spanned<'a, &'a str>>,
}

pub struct Spanned<'a, T> {
pub value: T,
pub range: Range<'a>,
}
20 changes: 16 additions & 4 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use anyhow::Result;
use ast::builder::build_ast;
use core::result::Result::Ok;
use files::Files;

Expand All @@ -13,7 +14,7 @@ fn main() -> Result<()> {
files.insert(
"main",
"
12 + 12;
enum colors: green blue red;
",
);

Expand All @@ -31,10 +32,9 @@ fn main() -> Result<()> {
};

let parser = parser::EarleyParser::default();
let ast = parser.parse(tokens);

let _ast = match &ast {
Ok(ast) => ast,
let parse_tree = match parser.parse(tokens) {
Ok(parse_tree) => parse_tree,
Err(diagnostics) => {
diagnostics
.iter()
Expand All @@ -43,5 +43,17 @@ fn main() -> Result<()> {
}
};

println!("{:#?}", parse_tree);

let ast = match build_ast(&parse_tree) {
Ok(ast) => ast,
Err(diagnostics) => {
diagnostics
.iter()
.for_each(|diagnostic| diagnostic.print(&files));
panic!("Failed to build AST");
}
};

Ok(())
}
59 changes: 19 additions & 40 deletions src/parser/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use grammar::{Grammar, NonTerminal, Term};

use crate::{
diagnostic::{Diagnostic, Error},
diagnostic::{Diagnostic, Error, Range},
scanner::token::Token,
};
use std::collections::HashSet;
Expand Down Expand Up @@ -42,6 +42,24 @@ pub enum ParseNode<'a> {
NonTerminal(NonTerminal, Vec<ParseNode<'a>>),
}

impl<'a> ParseNode<'a> {
pub fn range(&'a self) -> Range<'a> {
match self {
ParseNode::Terminal(token) => token.range.clone(),
ParseNode::NonTerminal(_, children) => {
let start = children.first().unwrap().range().position;
let end = children.last().unwrap().range();
let end = end.position + end.length;
Range {
file_id: children.first().unwrap().range().file_id,
position: start,
length: end - start,
}
}
}
}
}

impl<'a> std::fmt::Display for EarleyItem<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{} -> ", self.head)?;
Expand Down Expand Up @@ -267,42 +285,3 @@ impl<'a> EarleyParser<'a> {
}
}
}

#[cfg(test)]
mod test {
use crate::{files::Files, scanner::Scanner};

#[test]
pub fn test() {
let mut files = Files::default();
files.insert(
"main",
"
enum test: red green blue;
",
);

let scanner = Scanner::new(&files);
let tokens = match scanner.parse() {
Ok(tokens) => tokens,
Err(diagnostics) => {
for diagnostic in diagnostics {
diagnostic.print(&files);
}
panic!("Failed to scan");
}
};

let parser = super::EarleyParser::default();
match parser.parse(&tokens) {
Ok(tree) => {
println!("{:#?}", tree);
}
Err(diagnostics) => {
for diagnostic in diagnostics {
diagnostic.print(&files);
}
}
}
}
}

0 comments on commit 32635a8

Please sign in to comment.