blueprint-engine-parser 0.2.0

Starlark parser for Blueprint3
Documentation
use blueprint_engine_core::{BlueprintError, Result, SourceLocation, Span};
use blueprint_starlark_syntax::codemap::CodeMap;
use blueprint_starlark_syntax::dialect::{Dialect, DialectTypes};
use blueprint_starlark_syntax::syntax::module::AstModuleFields;
use blueprint_starlark_syntax::syntax::AstModule;

pub use blueprint_starlark_syntax::syntax::ast::{
    AssignOp, AssignTarget, AssignTargetP, AstArgument, AstExpr, AstLiteral, AstParameter,
    AstPayload, AstStmt, Clause, Expr, ExprP, ForClause, Parameter, ParameterP, Stmt, StmtP,
};
pub use blueprint_starlark_syntax::syntax::def::{DefParam, DefParams};

pub struct ParsedModule {
    pub codemap: CodeMap,
    pub statement: AstStmt,
}

impl ParsedModule {
    pub fn statements(&self) -> &AstStmt {
        &self.statement
    }
}

pub fn parse(filename: &str, content: &str) -> Result<ParsedModule> {
    let dialect = Dialect {
        enable_f_strings: true,
        enable_lambda: true,
        enable_keyword_only_arguments: true,
        enable_top_level_stmt: true,
        enable_types: DialectTypes::Enable,
        ..Dialect::Standard
    };

    match AstModule::parse(filename, content.to_owned(), &dialect) {
        Ok(module) => {
            let (codemap, statement, _dialect, _) = module.into_parts();
            Ok(ParsedModule { codemap, statement })
        }
        Err(e) => {
            let location = SourceLocation {
                file: Some(filename.to_string()),
                line: 1,
                column: 1,
                span: None,
            };
            Err(BlueprintError::ParseError {
                location,
                message: e.to_string(),
            })
        }
    }
}

pub fn get_location(
    codemap: &CodeMap,
    span: blueprint_starlark_syntax::codemap::Span,
) -> SourceLocation {
    let file_span = codemap.file_span(span);
    let loc = file_span.resolve();
    SourceLocation {
        file: Some(loc.file.clone()),
        line: loc.span.begin.line + 1,
        column: loc.span.begin.column + 1,
        span: Some(Span {
            start: span.begin().get() as usize,
            end: span.end().get() as usize,
        }),
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_parse_simple() {
        let result = parse("test.star", "x = 1 + 2");
        assert!(result.is_ok());
    }

    #[test]
    fn test_parse_function() {
        let code = r#"
def hello(name):
    print("Hello, " + name)
"#;
        let result = parse("test.star", code);
        assert!(result.is_ok());
    }

    #[test]
    fn test_parse_lambda() {
        let result = parse("test.star", "f = lambda x: x * 2");
        assert!(result.is_ok());
    }

    #[test]
    fn test_parse_error() {
        let result = parse("test.star", "x = ");
        assert!(result.is_err());
    }
}