diff --git a/src/ast/builder.rs b/src/ast/builder.rs index e69de29..e7dc476 100644 --- a/src/ast/builder.rs +++ b/src/ast/builder.rs @@ -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, Vec>> { + 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::, _>>() + { + 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, Vec>> { + match parse_tree { + ParseNode::NonTerminal(NonTerminal::RootItems, children) => { + let root_items = children + .iter() + .map(|child| build_ast(child)) + .collect::, _>>()?; + + 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() {} +} diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 5575a85..3d2c128 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -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>, +} + +pub struct Spanned<'a, T> { + pub value: T, + pub range: Range<'a>, +} diff --git a/src/main.rs b/src/main.rs index 70ee375..cf606e7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,5 @@ use anyhow::Result; +use ast::builder::build_ast; use core::result::Result::Ok; use files::Files; @@ -13,7 +14,7 @@ fn main() -> Result<()> { files.insert( "main", " - 12 + 12; + enum colors: green blue red; ", ); @@ -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() @@ -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(()) } diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 2502787..1569376 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -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; @@ -42,6 +42,24 @@ pub enum ParseNode<'a> { NonTerminal(NonTerminal, Vec>), } +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)?; @@ -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); - } - } - } - } -}