galvan-into-ast 0.0.3

CST to AST conversion for the Galvan programming language
Documentation
use galvan_ast::{Ast, Point, RootItem, SegmentedAsts, Span, ToplevelItem};
use galvan_files::Source;
use galvan_parse::*;

mod cursor_macro;
mod items;
mod modifiers;
mod result;

use result::CursorUtil;
pub use result::{AstError, AstResult};

pub trait IntoAst {
    fn try_into_ast(self, source: Source) -> AstResult;
}

pub trait SourceIntoAst {
    fn try_into_ast(self) -> AstResult;
}

pub trait ReadCursor: Sized {
    fn read_cursor(cursor: &mut TreeCursor<'_>, source: &str) -> Result<Self, AstError>;
}

impl SourceIntoAst for Source {
    fn try_into_ast(self) -> AstResult {
        let parsed = parse_source(&self)?;
        parsed.try_into_ast(self)
    }
}

impl IntoAst for ParseTree {
    fn try_into_ast(self, source: Source) -> AstResult {
        let root = self.root_node();
        let mut cursor = root.walk();

        let mut ast = Ast {
            toplevel: vec![],
            source,
        };

        if cursor.child() {
            let item = RootItem::read_cursor(&mut cursor, &ast.source)?;

            ast.toplevel.push(item);

            while cursor.next() {
                let item = RootItem::read_cursor(&mut cursor, &ast.source)?;

                ast.toplevel.push(item);
            }
        }

        Ok(ast)
    }
}

pub trait SegmentAst {
    fn segmented(self) -> Result<SegmentedAsts, AstError>;
}

impl SegmentAst for Ast {
    fn segmented(self) -> Result<SegmentedAsts, AstError> {
        let mut types = Vec::new();
        let mut functions = Vec::new();
        let mut tests = Vec::new();
        let mut main = None;

        for item in self.toplevel {
            match item {
                RootItem::Type(item) => types.push(ToplevelItem {
                    item,
                    source: self.source.clone(),
                }),
                RootItem::Fn(item) => functions.push(ToplevelItem {
                    item,
                    source: self.source.clone(),
                }),
                RootItem::Test(item) => tests.push(ToplevelItem {
                    item,
                    source: self.source.clone(),
                }),
                RootItem::Main(item) => {
                    if main.is_some() {
                        return Err(AstError::DuplicateMain);
                    }

                    main = Some(ToplevelItem {
                        item,
                        source: self.source.clone(),
                    })
                }
            }
        }

        Ok(SegmentedAsts {
            types,
            functions,
            tests,
            main,
        })
    }
}

impl SegmentAst for Vec<Ast> {
    fn segmented(self) -> Result<SegmentedAsts, AstError> {
        let mut types = Vec::new();
        let mut functions = Vec::new();
        let mut tests = Vec::new();
        let mut main = None;
        let segmented = self.into_iter().map(SegmentAst::segmented);

        for ast in segmented {
            let ast = ast?;
            types.extend(ast.types);
            functions.extend(ast.functions);
            tests.extend(ast.tests);
            if let Some(main_decl) = ast.main {
                if main.is_some() {
                    return Err(AstError::DuplicateMain);
                }

                main = Some(main_decl);
            }
        }

        Ok(SegmentedAsts {
            types,
            functions,
            tests,
            main,
        })
    }
}

pub trait SpanExt {
    fn from_node(node: Node<'_>) -> Span;
}

impl SpanExt for Span {
    fn from_node(node: Node<'_>) -> Span {
        let start_byte = node.start_byte();
        let end_byte = node.end_byte();
        let start_point = node.start_position();
        let end_point = node.end_position();

        let start = Point {
            row: start_point.row,
            col: start_point.column,
        };
        let end = Point {
            row: end_point.row,
            col: end_point.column,
        };

        Span {
            range: (start_byte, end_byte),
            start,
            end,
        }
    }
}