mod error;
mod expr;
mod stmt;
use crate::ast::*;
use atoxide_lexer::{Lexer, Token};
use chumsky::input::Input;
use chumsky::prelude::*;
pub use error::{ParseError, format_errors};
pub fn parse(source: &str) -> (Option<File>, Vec<ParseError>) {
let lexer = Lexer::new(source);
let tokens: Vec<Token> = lexer.collect();
let len = source.len();
let token_spans: Vec<(Token, SimpleSpan)> = tokens
.into_iter()
.map(|t| {
let sp: SimpleSpan = (t.span.start..t.span.end).into();
(t, sp)
})
.collect();
let eoi: SimpleSpan = (len..len).into();
let input = token_spans.as_slice().map(eoi, |(t, s)| (t, s));
let (ast, errors) = stmt::file_parser().parse(input).into_output_errors();
let parse_errors: Vec<ParseError> = errors
.iter()
.map(|e| ParseError::from_rich(e, source))
.collect();
(ast, parse_errors)
}
pub fn parse_with_errors(source: &str, filename: &str) -> (Option<File>, Option<String>) {
let (ast, errors) = parse(source);
let error_string = if errors.is_empty() {
None
} else {
Some(format_errors(&errors, source, filename))
};
(ast, error_string)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_simple_module() {
let source = "module M:\n pass\n";
let (ast, errors) = parse(source);
assert!(errors.is_empty(), "Errors: {:?}", errors);
assert!(ast.is_some());
let file = ast.unwrap();
assert_eq!(file.statements.len(), 1);
if let Statement::BlockDef(block) = &file.statements[0] {
assert_eq!(block.kind, BlockKind::Module);
assert_eq!(block.name.name, "M");
} else {
panic!("Expected block definition");
}
}
#[test]
fn test_parse_import() {
let source = "import ElectricPower\n";
let (ast, errors) = parse(source);
assert!(errors.is_empty(), "Errors: {:?}", errors);
assert!(ast.is_some());
}
#[test]
fn test_parse_from_import() {
let source = "from \"path/to/file.ato\" import Module\n";
let (ast, errors) = parse(source);
assert!(errors.is_empty(), "Errors: {:?}", errors);
assert!(ast.is_some());
}
#[test]
fn test_error_recovery() {
let source = "module Bad\n pass\n";
let (ast, errors) = parse(source);
assert!(!errors.is_empty(), "Should have parse errors");
let _ = ast; }
#[test]
fn test_parse_connection() {
let source = "module M:\n a ~ b\n";
let (ast, errors) = parse(source);
assert!(errors.is_empty(), "Errors: {:?}", errors);
assert!(ast.is_some());
}
#[test]
fn test_parse_assignment() {
let source = "module M:\n x = 5\n";
let (ast, errors) = parse(source);
assert!(errors.is_empty(), "Errors: {:?}", errors);
assert!(ast.is_some());
}
#[test]
fn test_parse_physical_quantity() {
let source = "module M:\n x = 10kohm +/- 5%\n";
let (ast, errors) = parse(source);
assert!(errors.is_empty(), "Errors: {:?}", errors);
assert!(ast.is_some());
}
}