feat: Add AST emitter and comment support

Introduces a new AST emitter module responsible for serializing AST
nodes back into Myc source code. This emitter preserves leading comments
and whitespace, enabling a round-trip guarantee for the parser and
emitter.

The documentation in `docs/BNF.md` has been updated to reflect the new
syntax rules for comments, including single-line comments (`;`) and
documentation comments (`;;`). The rules clarify that comments must be
whole lines and that inline comments are disallowed.

The lexer and AST node structures have been updated to accommodate
`CommentLine` variants, allowing for different types of comments
(regular, documentation, and blank lines) to be stored and processed.

A new test suite `tests/comment_roundtrip.rs` has been added to verify
the round-trip property of the parser and emitter across all example
`.myc` files. This test ensures that parsing source code, emitting it,
and then parsing the emitted code again results in the same output
string.

The `ast.rs` binary now includes a `--emit` flag that uses the new
emitter to output the parsed source code, facilitating debugging and
testing of the emitter itself.
This commit is contained in:
2026-03-25 19:15:40 +01:00
parent ee89d2330d
commit e757b453a4
10 changed files with 501 additions and 32 deletions
+17 -1
View File
@@ -1,5 +1,7 @@
use clap::Parser;
use myc::ast::compiler::emitter;
use myc::ast::environment::Environment;
use myc::ast::parser::Parser as MycParser;
use myc::utils::tester;
use std::fs;
use std::io::{self, IsTerminal, Read};
@@ -43,6 +45,10 @@ struct Cli {
/// Start MCP server (stdio transport, for LLM integration)
#[arg(long)]
mcp: bool,
/// Emit the parsed source back as Myc code (round-trip test)
#[arg(long)]
emit: bool,
}
fn main() {
@@ -108,7 +114,17 @@ fn main() {
};
if let Some(content) = source {
if cli.dump {
if cli.emit {
let mut parser = MycParser::new(&content);
let syntax_ast = parser.parse_expression();
if parser.diagnostics.has_errors() {
for diag in &parser.diagnostics.items {
eprintln!("ERROR: {}", diag.message);
}
std::process::exit(1);
}
print!("{}", emitter::emit(&syntax_ast));
} else if cli.dump {
match env.dump_ast(&content) {
Ok(dump) => println!("{}", dump),
Err(e) => eprintln!("Error dumping AST: {}", e),