Files
NexaCore/crates/nxc-frontend/tests/parser_tests.rs
nessi dfd2f10234 feat: add diagnostic system and improve CLI with check command
Implement comprehensive diagnostic reporting system:
- Add Diagnostic struct with severity levels and span-based error tracking
- Add diagnostic rendering with source context and caret positioning
- Replace ParseError with diagnostic collection in lexer and parser
- Add LexResult and ParseResult types to carry diagnostics

Enhance driver crate with frontend output:
- Replace CompileResult with FrontendOutput containing diagnostics
- Add has_errors() and render_diagnostics() methods
- Add AstSummary for
2026-04-06 17:07:50 +02:00

181 lines
4.8 KiB
Rust

use nxc_frontend::{
BinaryOp, ExprKind, Item, Lexer, Literal, Parser, StmtKind, UnaryOp,
};
fn parse(source: &str) -> nxc_frontend::ParseResult {
let lexed = Lexer::new(source).lex();
assert!(
lexed.diagnostics.is_empty(),
"unexpected lexer diagnostics: {:?}",
lexed.diagnostics
);
Parser::new(lexed.tokens).parse_module()
}
#[test]
fn parses_function_with_if_else_and_returns() {
let source = "\
fn classify(value: Int) -> Int:
if value > 10:
return 1
else:
return 0
";
let parsed = parse(source);
assert!(parsed.diagnostics.is_empty(), "{:?}", parsed.diagnostics);
assert_eq!(parsed.module.items.len(), 1);
let Item::Function(function) = &parsed.module.items[0] else {
panic!("expected function item");
};
assert_eq!(function.name, "classify");
assert_eq!(function.params.len(), 1);
assert_eq!(function.body.statements.len(), 1);
let StmtKind::If(if_stmt) = &function.body.statements[0].kind else {
panic!("expected if statement");
};
match &if_stmt.condition.kind {
ExprKind::Binary { op, .. } => assert_eq!(*op, BinaryOp::Greater),
other => panic!("unexpected condition: {other:?}"),
}
assert!(if_stmt.else_block.is_some());
}
#[test]
fn respects_binary_operator_precedence() {
let source = "\
fn main() -> Int:
return 1 + 2 * 3 == 7 || false
";
let parsed = parse(source);
assert!(parsed.diagnostics.is_empty(), "{:?}", parsed.diagnostics);
let Item::Function(function) = &parsed.module.items[0] else {
panic!("expected function item");
};
let StmtKind::Return(Some(expr)) = &function.body.statements[0].kind else {
panic!("expected return statement");
};
let ExprKind::Binary { left, op, right } = &expr.kind else {
panic!("expected binary expression");
};
assert_eq!(*op, BinaryOp::LogicalOr);
let ExprKind::Binary {
left: equality_left,
op: equality_op,
right: equality_right,
} = &left.kind
else {
panic!("expected equality expression");
};
assert_eq!(*equality_op, BinaryOp::Equal);
let ExprKind::Binary {
left: add_left,
op: add_op,
right: add_right,
} = &equality_left.kind
else {
panic!("expected additive expression");
};
assert_eq!(*add_op, BinaryOp::Add);
match &add_left.kind {
ExprKind::Literal(Literal::Integer(1)) => {}
other => panic!("unexpected left additive operand: {other:?}"),
}
let ExprKind::Binary {
left: mul_left,
op: mul_op,
right: mul_right,
} = &add_right.kind
else {
panic!("expected multiplicative expression");
};
assert_eq!(*mul_op, BinaryOp::Multiply);
match &mul_left.kind {
ExprKind::Literal(Literal::Integer(2)) => {}
other => panic!("unexpected left multiplicative operand: {other:?}"),
}
match &mul_right.kind {
ExprKind::Literal(Literal::Integer(3)) => {}
other => panic!("unexpected right multiplicative operand: {other:?}"),
}
match &equality_right.kind {
ExprKind::Literal(Literal::Integer(7)) => {}
other => panic!("unexpected equality right operand: {other:?}"),
}
match &right.kind {
ExprKind::Literal(Literal::Bool(false)) => {}
other => panic!("unexpected logical-or right operand: {other:?}"),
}
}
#[test]
fn parses_grouping_unary_and_calls() {
let source = "\
fn main() -> Int:
return -(compute(1, 2) + 3)
";
let parsed = parse(source);
assert!(parsed.diagnostics.is_empty(), "{:?}", parsed.diagnostics);
let Item::Function(function) = &parsed.module.items[0] else {
panic!("expected function item");
};
let StmtKind::Return(Some(expr)) = &function.body.statements[0].kind else {
panic!("expected return statement");
};
let ExprKind::Unary { op, expr: inner } = &expr.kind else {
panic!("expected unary expression");
};
assert_eq!(*op, UnaryOp::Negate);
let ExprKind::Group(grouped) = &inner.kind else {
panic!("expected grouped expression");
};
let ExprKind::Binary { left, op, .. } = &grouped.kind else {
panic!("expected additive expression");
};
assert_eq!(*op, BinaryOp::Add);
let ExprKind::Call { args, .. } = &left.kind else {
panic!("expected call expression");
};
assert_eq!(args.len(), 2);
}
#[test]
fn recovers_and_reports_syntax_errors() {
let source = "\
fn broken(value: Int) -> Int
let x = 1
struct Config:
port: Int
";
let parsed = parse(source);
assert!(!parsed.diagnostics.is_empty());
assert_eq!(parsed.module.items.len(), 1);
let Item::Struct(struct_decl) = &parsed.module.items[0] else {
panic!("expected recovered struct declaration");
};
assert_eq!(struct_decl.name, "Config");
}