Implement evaluator materializer and CLI
This commit is contained in:
@@ -1,3 +1,163 @@
|
||||
fn main() {
|
||||
println!("Decodal {}", decodal_core::version());
|
||||
use std::{env, fs, process::ExitCode};
|
||||
|
||||
use decodal_core::{Data, Diagnostic, Engine, parse_source};
|
||||
|
||||
fn main() -> ExitCode {
|
||||
match run() {
|
||||
Ok(()) => ExitCode::SUCCESS,
|
||||
Err(error) => {
|
||||
print_diagnostic(&error);
|
||||
ExitCode::FAILURE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn run() -> Result<(), Diagnostic> {
|
||||
let mut args = env::args().skip(1);
|
||||
let first = args.next().unwrap_or_else(|| String::from("--help"));
|
||||
match first.as_str() {
|
||||
"--help" | "-h" => {
|
||||
print_help();
|
||||
Ok(())
|
||||
}
|
||||
"--version" | "-V" => {
|
||||
println!("Decodal {}", decodal_core::version());
|
||||
Ok(())
|
||||
}
|
||||
"check" => {
|
||||
let path = args.next().unwrap_or_else(|| String::from("-"));
|
||||
let source = read_source(&path)?;
|
||||
let parsed = parse_source(&source)?;
|
||||
let mut engine = Engine::from_parse(parsed.ast, parsed.root);
|
||||
let value = engine.eval_root()?;
|
||||
engine.materialize(&value)?;
|
||||
println!("ok");
|
||||
Ok(())
|
||||
}
|
||||
"eval" | "materialize" => {
|
||||
let path = args.next().unwrap_or_else(|| String::from("-"));
|
||||
materialize_file(&path)
|
||||
}
|
||||
path => materialize_file(path),
|
||||
}
|
||||
}
|
||||
|
||||
fn materialize_file(path: &str) -> Result<(), Diagnostic> {
|
||||
let source = read_source(path)?;
|
||||
let parsed = parse_source(&source)?;
|
||||
let mut engine = Engine::from_parse(parsed.ast, parsed.root);
|
||||
let value = engine.eval_root()?;
|
||||
let data = engine.materialize(&value)?;
|
||||
print_data(&data, 0);
|
||||
println!();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read_source(path: &str) -> Result<String, Diagnostic> {
|
||||
if path == "-" {
|
||||
use std::io::Read;
|
||||
let mut source = String::new();
|
||||
std::io::stdin()
|
||||
.read_to_string(&mut source)
|
||||
.map_err(|error| {
|
||||
Diagnostic::new(
|
||||
decodal_core::DiagnosticKind::Import,
|
||||
decodal_core::Span::default(),
|
||||
format!("failed to read stdin: {error}"),
|
||||
)
|
||||
})?;
|
||||
Ok(source)
|
||||
} else {
|
||||
fs::read_to_string(path).map_err(|error| {
|
||||
Diagnostic::new(
|
||||
decodal_core::DiagnosticKind::Import,
|
||||
decodal_core::Span::default(),
|
||||
format!("failed to read `{path}`: {error}"),
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn print_help() {
|
||||
println!("Decodal - Deferred Constraint Data Language");
|
||||
println!();
|
||||
println!("Usage:");
|
||||
println!(" decodal <file.dcdl> Materialize a DCDL file");
|
||||
println!(" decodal eval <file.dcdl> Materialize a DCDL file");
|
||||
println!(" decodal check <file.dcdl> Evaluate and materialize without printing data");
|
||||
println!(" decodal - Read DCDL source from stdin");
|
||||
}
|
||||
|
||||
fn print_diagnostic(error: &Diagnostic) {
|
||||
eprintln!(
|
||||
"error[{kind:?}] {start}..{end}: {message}",
|
||||
kind = error.kind,
|
||||
start = error.span.start,
|
||||
end = error.span.end,
|
||||
message = error.message,
|
||||
);
|
||||
}
|
||||
|
||||
fn print_data(data: &Data, indent: usize) {
|
||||
match data {
|
||||
Data::String(value) => print_string(value),
|
||||
Data::Int(value) => print!("{value}"),
|
||||
Data::Float(value) => print!("{value}"),
|
||||
Data::Bool(value) => print!("{value}"),
|
||||
Data::Array(items) => {
|
||||
print!("[");
|
||||
if !items.is_empty() {
|
||||
println!();
|
||||
for (index, item) in items.iter().enumerate() {
|
||||
print_indent(indent + 2);
|
||||
print_data(item, indent + 2);
|
||||
if index + 1 != items.len() {
|
||||
print!(",");
|
||||
}
|
||||
println!();
|
||||
}
|
||||
print_indent(indent);
|
||||
}
|
||||
print!("]");
|
||||
}
|
||||
Data::Object(fields) => {
|
||||
print!("{{");
|
||||
if !fields.is_empty() {
|
||||
println!();
|
||||
for (index, field) in fields.iter().enumerate() {
|
||||
print_indent(indent + 2);
|
||||
print_string(&field.name);
|
||||
print!(": ");
|
||||
print_data(&field.value, indent + 2);
|
||||
if index + 1 != fields.len() {
|
||||
print!(",");
|
||||
}
|
||||
println!();
|
||||
}
|
||||
print_indent(indent);
|
||||
}
|
||||
print!("}}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn print_indent(indent: usize) {
|
||||
for _ in 0..indent {
|
||||
print!(" ");
|
||||
}
|
||||
}
|
||||
|
||||
fn print_string(value: &str) {
|
||||
print!("\"");
|
||||
for ch in value.chars() {
|
||||
match ch {
|
||||
'"' => print!("\\\""),
|
||||
'\\' => print!("\\\\"),
|
||||
'\n' => print!("\\n"),
|
||||
'\r' => print!("\\r"),
|
||||
'\t' => print!("\\t"),
|
||||
ch => print!("{ch}"),
|
||||
}
|
||||
}
|
||||
print!("\"");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user