-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: DSL CLI & Helpful Error Reporting (#35)
## Problem We need a CLI to be able to compile our DSL. More importantly, we need useful error messages when the programmer did a (syntax, semantic, or other) mistake. ## Example Best described with a picture. ``` fn (pred: Predicate) remap(map: {I64 : I64)}) = match predicate | ColumnRef(idx) => ColumnRef(map(idx)) \ _ => predicate -> apply_children(child => rewrite_column_refs(child, map)) [rule] fn (expr: Logical) join_commute = match expr \ Join(left, right, Inner, cond) -> let right_indices = 0.right.schema_len, left_indices = 0..left.schema_len, remapping = left_indices.map(i => (i, i + right_len)) ++ right_indices.map(i => (left_len + i, i)).to_map, in Project( Join(right, left, Inner, cond.remap(remapping)), right_indices.map(i => ColumnRef(i)).to_array ) ```  ## Summary of changes - Unified error management for the compilation process. - Integrate a CLI & playground in `optd-dsl` where people can experiment with some code, print the AST, etc.
- Loading branch information
Showing
27 changed files
with
769 additions
and
226 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,3 @@ | ||
pub mod hir; | ||
pub mod semantic; | ||
pub mod r#type; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
use ariadne::{Report, Source}; | ||
|
||
use crate::utils::{error::Diagnose, span::Span}; | ||
|
||
#[derive(Debug)] | ||
pub struct SemanticError {} | ||
|
||
impl Diagnose for SemanticError { | ||
fn report(&self) -> Report<Span> { | ||
todo!() | ||
} | ||
|
||
fn source(&self) -> (String, Source) { | ||
todo!() | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
pub mod error; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
use ariadne::{Report, Source}; | ||
|
||
use crate::utils::{error::Diagnose, span::Span}; | ||
|
||
#[derive(Debug)] | ||
pub struct TypeError {} | ||
|
||
impl Diagnose for TypeError { | ||
fn report(&self) -> Report<Span> { | ||
todo!() | ||
} | ||
|
||
fn source(&self) -> (String, Source) { | ||
todo!() | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
pub mod error; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
data LogicalProps(schema_len: I64) | ||
|
||
data Scalar with | ||
| ColumnRef(idx: Int64) | ||
| Literal with | ||
| IntLiteral(value: Int64) | ||
| StringLiteral(value: String) | ||
| BoolLiteral(value: Bool) | ||
\ NullLiteral | ||
| Arithmetic with | ||
| Mult(left: Scalar, right: Scalar) | ||
| Add(left: Scalar, right: Scalar) | ||
| Sub(left: Scalar, right: Scalar) | ||
\ Div(left: Scalar, right: Scalar) | ||
| Predicate with | ||
| And(children: [Predicate]) | ||
| Or(children: [Predicate]) | ||
| Not(child: Predicate) | ||
| Equals(left: Scalar, right: Scalar) | ||
| NotEquals(left: Scalar, right: Scalar) | ||
| LessThan(left: Scalar, right: Scalar) | ||
| LessThanEqual(left: Scalar, right: Scalar) | ||
| GreaterThan(left: Scalar, right: Scalar) | ||
| GreaterThanEqual(left: Scalar, right: Scalar) | ||
| IsNull(expr: Scalar) | ||
\ IsNotNull(expr: Scalar) | ||
| Function with | ||
| Cast(expr: Scalar, target_type: String) | ||
| Substring(str: Scalar, start: Scalar, length: Scalar) | ||
\ Concat(args: [Scalar]) | ||
\ AggregateExpr with | ||
| Sum(expr: Scalar) | ||
| Count(expr: Scalar) | ||
| Min(expr: Scalar) | ||
| Max(expr: Scalar) | ||
\ Avg(expr: Scalar) | ||
|
||
data Logical with | ||
| Scan(table_name: String) | ||
| Filter(child: Logical, cond: Predicate) | ||
| Project(child: Logical, exprs: [Scalar]) | ||
| Join( | ||
left: Logical, | ||
right: Logical, | ||
typ: JoinType, | ||
cond: Predicate | ||
) | ||
\ Aggregate( | ||
child: Logical, | ||
group_by: [Scalar], | ||
aggregates: [AggregateExpr] | ||
) | ||
|
||
data Physical with | ||
| Scan(table_name: String) | ||
| Filter(child: Physical, cond: Predicate) | ||
| Project(child: Physical, exprs: [Scalar]) | ||
| Join with | ||
| HashJoin( | ||
build_side: Physical, | ||
probe_side: Physical, | ||
typ: String, | ||
cond: Predicate | ||
) | ||
| MergeJoin( | ||
left: Physical, | ||
right: Physical, | ||
typ: String, | ||
cond: Predicate | ||
) | ||
\ NestedLoopJoin( | ||
outer: Physical, | ||
inner: Physical, | ||
typ: String, | ||
cond: Predicate | ||
) | ||
| Aggregate( | ||
child: Physical, | ||
group_by: [Scalar], | ||
aggregates: [AggregateExpr] | ||
) | ||
\ Sort( | ||
child: Physical, | ||
order_by: [(Scalar, SortOrder)] | ||
) | ||
|
||
data JoinType with | ||
| Inner | ||
| Left | ||
| Right | ||
| Full | ||
\ Semi | ||
|
||
[rust] | ||
fn (expr: Scalar) apply_children(f: Scalar => Scalar) = () | ||
|
||
fn (pred: Predicate) remap(map: {I64 : I64)}) = | ||
match predicate | ||
| ColumnRef(idx) => ColumnRef(map(idx)) | ||
\ _ => predicate -> apply_children(child => rewrite_column_refs(child, map)) | ||
|
||
[rule] | ||
fn (expr: Logical) join_commute = match expr | ||
\ Join(left, right, Inner, cond) -> | ||
let | ||
right_indices = 0.right.schema_len, | ||
left_indices = 0..left.schema_len, | ||
remapping = left_indices.map(i => (i, i + right_len)) ++ | ||
right_indices.map(i => (left_len + i, i)).to_map, | ||
in | ||
Project( | ||
Join(right, left, Inner, cond.remap(remapping)), | ||
right_indices.map(i => ColumnRef(i)).to_array | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
//! CLI tool for the Optimizer DSL | ||
//! | ||
//! This tool provides a command-line interface for the Optimizer DSL compiler. | ||
//! | ||
//! # Usage | ||
//! | ||
//! ``` | ||
//! # Parse a DSL file (validate syntax): | ||
//! optd parse path/to/file.op | ||
//! | ||
//! # Parse a file and print the AST: | ||
//! optd parse path/to/file.op --print-ast | ||
//! | ||
//! # Get help: | ||
//! optd --help | ||
//! optd parse --help | ||
//! ``` | ||
//! | ||
//! When developing, you can run through cargo: | ||
//! | ||
//! ``` | ||
//! cargo run -- parse examples/example.dsl | ||
//! cargo run -- parse examples/example.dsl --print-ast | ||
//! ``` | ||
use clap::{Parser, Subcommand}; | ||
use optd_dsl::compiler::compile::{parse, CompileOptions}; | ||
use optd_dsl::utils::error::Diagnose; | ||
use std::fs; | ||
use std::path::PathBuf; | ||
|
||
#[derive(Parser)] | ||
#[command( | ||
name = "optd", | ||
about = "Optimizer DSL compiler and toolchain", | ||
version, | ||
author | ||
)] | ||
struct Cli { | ||
#[command(subcommand)] | ||
command: Commands, | ||
} | ||
|
||
#[derive(Subcommand)] | ||
enum Commands { | ||
/// Parse a DSL file and validate its syntax | ||
Parse { | ||
/// Input file to parse | ||
#[arg(value_name = "FILE")] | ||
input: PathBuf, | ||
|
||
/// Print the AST in a readable format | ||
#[arg(long)] | ||
print_ast: bool, | ||
}, | ||
} | ||
|
||
fn main() -> Result<(), Box<dyn std::error::Error>> { | ||
let cli = Cli::parse(); | ||
|
||
match &cli.command { | ||
Commands::Parse { input, print_ast } => { | ||
println!("Parsing file: {}", input.display()); | ||
|
||
// Improve file reading error handling | ||
let source = match fs::read_to_string(input) { | ||
Ok(content) => content, | ||
Err(e) => { | ||
if e.kind() == std::io::ErrorKind::NotFound { | ||
eprintln!("❌ Error: File not found: {}", input.display()); | ||
eprintln!( | ||
"Please check that the file exists and you have correct permissions." | ||
); | ||
} else { | ||
eprintln!("❌ Error reading file: {}", e); | ||
} | ||
std::process::exit(1); | ||
} | ||
}; | ||
|
||
let options = CompileOptions { | ||
source_path: input.to_string_lossy().to_string(), | ||
}; | ||
|
||
match parse(&source, &options) { | ||
Ok(ast) => { | ||
println!("✅ Parse successful!"); | ||
if *print_ast { | ||
println!("\nAST Structure:"); | ||
println!("{:#?}", ast); | ||
} | ||
} | ||
Err(errors) => { | ||
eprintln!("❌ Parse failed with {} errors:", errors.len()); | ||
for error in errors { | ||
error.print(std::io::stderr())?; | ||
} | ||
std::process::exit(1); | ||
} | ||
} | ||
} | ||
} | ||
|
||
Ok(()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
use crate::lexer::lex::lex; | ||
use crate::parser::ast::Module; | ||
use crate::parser::module::parse_module; | ||
use crate::utils::error::CompileError; | ||
|
||
/// Compilation options for the DSL | ||
pub struct CompileOptions { | ||
/// Path to the main module source file | ||
pub source_path: String, | ||
} | ||
|
||
/// Parse DSL source code to AST | ||
/// | ||
/// This function performs lexing and parsing stages of compilation, | ||
/// returning either the parsed AST Module or collected errors. | ||
/// | ||
/// # Arguments | ||
/// * `source` - The source code to parse | ||
/// * `options` - Compilation options including source path | ||
/// | ||
/// # Returns | ||
/// * `Result<Module, Vec<CompileError>>` - The parsed AST or errors | ||
pub fn parse(source: &str, options: &CompileOptions) -> Result<Module, Vec<CompileError>> { | ||
let mut errors = Vec::new(); | ||
|
||
// Step 1: Lexing | ||
let (tokens_opt, lex_errors) = lex(source, &options.source_path); | ||
errors.extend(lex_errors); | ||
|
||
match tokens_opt { | ||
Some(tokens) => { | ||
// Step 2: Parsing | ||
let (ast_opt, parse_errors) = parse_module(tokens, source, &options.source_path); | ||
errors.extend(parse_errors); | ||
|
||
match ast_opt { | ||
Some(ast) if errors.is_empty() => Ok(ast), | ||
_ => Err(errors), | ||
} | ||
} | ||
None => Err(errors), | ||
} | ||
} | ||
|
||
/// Compile DSL source code to HIR | ||
/// | ||
/// This function performs the full compilation pipeline including lexing, | ||
/// parsing, and semantic analysis to produce HIR. | ||
/// | ||
/// # Arguments | ||
/// * `source` - The source code to compile | ||
/// * `options` - Compilation options including source path | ||
/// | ||
/// # Returns | ||
/// * `Result<HIR, Vec<CompileError>>` - The compiled HIR or errors | ||
pub fn compile( | ||
source: &str, | ||
options: &CompileOptions, | ||
) -> Result<crate::analyzer::hir::HIR, Vec<CompileError>> { | ||
// Step 1 & 2: Parse to AST | ||
let _ast = parse(source, options)?; | ||
|
||
// Step 3: Semantic analysis to HIR | ||
todo!("Implement semantic analysis to convert AST to HIR") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
pub mod compile; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,3 @@ | ||
pub(super) mod errors; | ||
pub(super) mod error; | ||
pub(super) mod macros; | ||
pub(super) mod streams; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.