From 22f6bc1ab9eb0f7396771f19addb42eca1037497 Mon Sep 17 00:00:00 2001 From: Hare Date: Thu, 9 Jul 2026 17:39:32 +0900 Subject: [PATCH] Add Decodal formatter --- crates/decodal-cli/src/main.rs | 37 +- crates/decodal-core/src/formatter.rs | 442 ++++++++++++++++++ crates/decodal-core/src/lib.rs | 2 + crates/decodal-wasm/src/lib.rs | 20 +- doc/manual/souce/components.md | 2 +- doc/manual/souce/formatting.md | 50 ++ doc/manual/souce/index.md | 9 +- packages/decodal-wasm/README.md | 12 +- packages/decodal-wasm/decodal_wasm.d.ts | 3 + packages/decodal-wasm/decodal_wasm.js | 19 + packages/decodal-wasm/decodal_wasm_bg.wasm | Bin 298960 -> 311824 bytes .../decodal-wasm/decodal_wasm_bg.wasm.d.ts | 1 + .../scripts/prepare-wasm-package.mjs | 12 +- site/decodal-site/src/lib/docs.js | 1 + site/decodal-site/src/pages/playground.astro | 1 + site/decodal-site/src/scripts/playground.js | 19 +- 16 files changed, 606 insertions(+), 24 deletions(-) create mode 100644 crates/decodal-core/src/formatter.rs create mode 100644 doc/manual/souce/formatting.md diff --git a/crates/decodal-cli/src/main.rs b/crates/decodal-cli/src/main.rs index f040243..525868e 100644 --- a/crates/decodal-cli/src/main.rs +++ b/crates/decodal-cli/src/main.rs @@ -6,7 +6,7 @@ use std::{ use decodal::{ Data, Diagnostic, DiagnosticKind, Engine, LoadedSource, SourceId, SourceLoader, Span, - format_diagnostic_with, + format_diagnostic_with, format_source_with_source_id, }; fn main() -> ExitCode { @@ -38,6 +38,17 @@ fn run() -> Result<(), String> { println!("ok"); Ok(()) } + "fmt" | "format" => { + let next = args.next().unwrap_or_else(|| String::from("-")); + if next == "--write" || next == "-w" { + let path = args + .next() + .ok_or_else(|| String::from("missing file path for `decodal fmt --write`"))?; + format_path(&path, true) + } else { + format_path(&next, false) + } + } "eval" | "materialize" => { let path = args.next().unwrap_or_else(|| String::from("-")); let data = materialize_path(&path)?; @@ -54,6 +65,21 @@ fn run() -> Result<(), String> { } } +fn format_path(path: &str, write: bool) -> Result<(), String> { + if write && path == "-" { + return Err(String::from("cannot use --write with stdin")); + } + let root = read_root_source(path).map_err(format_raw_diagnostic)?; + let formatted = format_source_with_source_id(SourceId(0), &root.source) + .map_err(|error| format_diagnostic_with_root(&error, &root.name))?; + if write { + fs::write(path, formatted).map_err(|error| format!("failed to write `{path}`: {error}"))?; + } else { + print!("{formatted}"); + } + Ok(()) +} + fn materialize_path(path: &str) -> Result { let root = read_root_source(path).map_err(format_raw_diagnostic)?; let root_name = root.name.clone(); @@ -143,10 +169,11 @@ fn print_help() { println!("Decodal - Deferred Constraint Data Language"); println!(); println!("Usage:"); - println!(" decodal Materialize a DCDL file"); - println!(" decodal eval Materialize a DCDL file"); - println!(" decodal check Evaluate and materialize without printing data"); - println!(" decodal - Read DCDL source from stdin"); + println!(" decodal Materialize a DCDL file"); + println!(" decodal eval Materialize a DCDL file"); + println!(" decodal check Evaluate and materialize without printing data"); + println!(" decodal fmt [--write] Format a DCDL file"); + println!(" decodal - Read DCDL source from stdin"); } fn format_diagnostic_with_root(error: &Diagnostic, root_name: &str) -> String { diff --git a/crates/decodal-core/src/formatter.rs b/crates/decodal-core/src/formatter.rs new file mode 100644 index 0000000..321d5d1 --- /dev/null +++ b/crates/decodal-core/src/formatter.rs @@ -0,0 +1,442 @@ +use alloc::string::{String, ToString}; +use core::fmt::Write; + +use crate::{ + Ast, Diagnostic, Expr, ExprId, Field, Literal, Param, SourceForm, Span, + ast::{BinaryOp, CompareOp, MatchArm, UnaryOp}, + diagnostic::Result, + parse_source_with_source_id, + span::SourceId, +}; + +const INDENT: usize = 4; + +pub fn format_source(source: &str) -> Result { + format_source_with_source_id(SourceId(0), source) +} + +pub fn format_source_with_source_id(source_id: SourceId, source: &str) -> Result { + reject_comments(source_id, source)?; + let parsed = parse_source_with_source_id(source_id, source)?; + Ok(Formatter::new(&parsed.ast).format_root(parsed.root, parsed.source_form)) +} + +struct Formatter<'a> { + ast: &'a Ast, +} + +impl<'a> Formatter<'a> { + fn new(ast: &'a Ast) -> Self { + Self { ast } + } + + fn format_root(&self, root: ExprId, source_form: SourceForm) -> String { + let mut out = String::new(); + match source_form { + SourceForm::Fields => { + if let Expr::Object(fields) = &self.ast.get(root).expr { + self.write_fields(&mut out, fields, 0); + } else { + self.write_expr(&mut out, root, 0, 0); + } + } + SourceForm::Expr => self.write_expr(&mut out, root, 0, 0), + } + if !out.ends_with('\n') { + out.push('\n'); + } + out + } + + fn write_expr(&self, out: &mut String, id: ExprId, indent: usize, parent_prec: u8) { + let expr = &self.ast.get(id).expr; + let prec = expr_prec(expr); + let paren = prec < parent_prec; + if paren { + out.push('('); + } + match expr { + Expr::Literal(literal) => self.write_literal(out, literal), + Expr::Ident(name) => out.push_str(name), + Expr::Object(fields) => self.write_object(out, fields, indent), + Expr::Array(items) => self.write_array(out, items, indent), + Expr::Let { bindings, body } => self.write_let(out, bindings, *body, indent), + Expr::Import(path) => { + out.push_str("import "); + write_quoted(out, path); + } + Expr::Path { base, field } => { + self.write_expr(out, *base, indent, prec); + out.push('.'); + out.push_str(field); + } + Expr::Call { callee, args } => { + self.write_expr(out, *callee, indent, prec); + out.push('('); + for (index, arg) in args.iter().enumerate() { + if index > 0 { + out.push_str(", "); + } + self.write_expr(out, *arg, indent, 0); + } + out.push(')'); + } + Expr::Function { params, body } => self.write_function(out, params, *body, indent), + Expr::Match { scrutinee, arms } => self.write_match(out, *scrutinee, arms, indent), + Expr::Unary { op, expr } => { + out.push_str(match op { + UnaryOp::Neg => "-", + UnaryOp::Not => "!", + }); + self.write_expr(out, *expr, indent, prec); + } + Expr::Binary { op, lhs, rhs } => { + self.write_expr(out, *lhs, indent, prec); + out.push(' '); + out.push_str(binary_op_text(*op)); + out.push(' '); + self.write_expr(out, *rhs, indent, prec + 1); + } + Expr::Default { base, fallback } => { + self.write_expr(out, *base, indent, prec); + out.push_str(" default "); + self.write_expr(out, *fallback, indent, prec + 1); + } + Expr::CompareConstraint { op, value } => { + out.push_str(compare_op_text(*op)); + out.push(' '); + self.write_expr(out, *value, indent, prec); + } + Expr::RegexConstraint(pattern) => { + out.push('/'); + out.push_str(pattern); + out.push('/'); + } + Expr::Wildcard => out.push('_'), + } + if paren { + out.push(')'); + } + } + + fn write_literal(&self, out: &mut String, literal: &Literal) { + match literal { + Literal::String(value) => write_quoted(out, value), + Literal::Int(value) => { + let _ = write!(out, "{value}"); + } + Literal::Float(value) => { + let text = value.to_string(); + out.push_str(&text); + if !text.contains('.') && !text.contains('e') && !text.contains('E') { + out.push_str(".0"); + } + } + Literal::Bool(value) => out.push_str(if *value { "true" } else { "false" }), + } + } + + fn write_object(&self, out: &mut String, fields: &[Field], indent: usize) { + if fields.is_empty() { + out.push_str("{}"); + return; + } + out.push('{'); + out.push('\n'); + self.write_fields(out, fields, indent + INDENT); + write_indent(out, indent); + out.push('}'); + } + + fn write_fields(&self, out: &mut String, fields: &[Field], indent: usize) { + for field in fields { + write_indent(out, indent); + self.write_field(out, field, indent); + out.push_str(";\n"); + } + } + + fn write_field(&self, out: &mut String, field: &Field, indent: usize) { + for (index, part) in field.path.iter().enumerate() { + if index > 0 { + out.push('.'); + } + out.push_str(part); + } + out.push_str(" = "); + self.write_expr(out, field.value, indent, 0); + } + + fn write_array(&self, out: &mut String, items: &[ExprId], indent: usize) { + if items.is_empty() { + out.push_str("[]"); + return; + } + if items.iter().all(|item| self.is_inline(*item)) { + out.push('['); + for (index, item) in items.iter().enumerate() { + if index > 0 { + out.push_str(", "); + } + self.write_expr(out, *item, indent, 0); + } + out.push(']'); + return; + } + out.push('['); + out.push('\n'); + for item in items { + write_indent(out, indent + INDENT); + self.write_expr(out, *item, indent + INDENT, 0); + out.push_str(",\n"); + } + write_indent(out, indent); + out.push(']'); + } + + fn write_let(&self, out: &mut String, bindings: &[Field], body: ExprId, indent: usize) { + out.push_str("let"); + out.push('\n'); + self.write_fields(out, bindings, indent + INDENT); + write_indent(out, indent); + out.push_str("in"); + out.push('\n'); + write_indent(out, indent + INDENT); + self.write_expr(out, body, indent + INDENT, 0); + } + + fn write_function(&self, out: &mut String, params: &[Param], body: ExprId, indent: usize) { + out.push('('); + for (index, param) in params.iter().enumerate() { + if index > 0 { + out.push_str(", "); + } + self.write_param(out, param, indent); + } + out.push(')'); + out.push_str(" =>"); + if self.is_inline(body) { + out.push(' '); + self.write_expr(out, body, indent, 0); + } else { + out.push('\n'); + write_indent(out, indent + INDENT); + self.write_expr(out, body, indent + INDENT, 0); + } + } + + fn write_param(&self, out: &mut String, param: &Param, indent: usize) { + out.push_str(¶m.name); + if let Some(constraint) = param.constraint { + out.push_str(": "); + self.write_expr(out, constraint, indent, 0); + } + } + + fn write_match(&self, out: &mut String, scrutinee: ExprId, arms: &[MatchArm], indent: usize) { + out.push_str("match "); + self.write_expr(out, scrutinee, indent, 0); + out.push_str(" {"); + if !arms.is_empty() { + out.push('\n'); + for arm in arms { + write_indent(out, indent + INDENT); + self.write_expr(out, arm.pattern, indent + INDENT, 0); + out.push_str(": "); + self.write_expr(out, arm.body, indent + INDENT, 0); + out.push_str(";\n"); + } + write_indent(out, indent); + } + out.push('}'); + } + + fn is_inline(&self, id: ExprId) -> bool { + match &self.ast.get(id).expr { + Expr::Literal(_) + | Expr::Ident(_) + | Expr::Import(_) + | Expr::RegexConstraint(_) + | Expr::Wildcard => true, + Expr::Path { base, .. } => self.is_inline(*base), + Expr::Call { callee, args } => { + self.is_inline(*callee) && args.iter().all(|arg| self.is_inline(*arg)) + } + Expr::Unary { expr, .. } => self.is_inline(*expr), + Expr::Binary { lhs, rhs, .. } => self.is_inline(*lhs) && self.is_inline(*rhs), + Expr::Default { base, fallback } => self.is_inline(*base) && self.is_inline(*fallback), + Expr::CompareConstraint { value, .. } => self.is_inline(*value), + Expr::Array(items) => items.iter().all(|item| self.is_inline(*item)), + Expr::Object(fields) => fields.is_empty(), + Expr::Let { .. } | Expr::Function { .. } | Expr::Match { .. } => false, + } + } +} + +fn expr_prec(expr: &Expr) -> u8 { + match expr { + Expr::Default { .. } => 1, + Expr::Binary { op, .. } => binary_prec(*op), + Expr::Unary { .. } | Expr::CompareConstraint { .. } => 17, + Expr::Path { .. } | Expr::Call { .. } => 19, + _ => 20, + } +} + +fn binary_prec(op: BinaryOp) -> u8 { + match op { + BinaryOp::Patch => 3, + BinaryOp::And => 5, + BinaryOp::LogicalOr => 7, + BinaryOp::LogicalAnd => 9, + BinaryOp::Equal + | BinaryOp::NotEqual + | BinaryOp::Greater + | BinaryOp::GreaterEqual + | BinaryOp::Less + | BinaryOp::LessEqual => 11, + BinaryOp::Concat => 12, + BinaryOp::Add | BinaryOp::Sub => 13, + BinaryOp::Mul | BinaryOp::Div => 15, + } +} + +fn binary_op_text(op: BinaryOp) -> &'static str { + match op { + BinaryOp::Add => "+", + BinaryOp::Sub => "-", + BinaryOp::Mul => "*", + BinaryOp::Div => "/", + BinaryOp::Concat => "++", + BinaryOp::Equal => "==", + BinaryOp::NotEqual => "!=", + BinaryOp::Greater => ">", + BinaryOp::GreaterEqual => ">=", + BinaryOp::Less => "<", + BinaryOp::LessEqual => "<=", + BinaryOp::LogicalAnd => "&&", + BinaryOp::LogicalOr => "||", + BinaryOp::And => "&", + BinaryOp::Patch => "//", + } +} + +fn compare_op_text(op: CompareOp) -> &'static str { + match op { + CompareOp::Gt => ">", + CompareOp::Gte => ">=", + CompareOp::Lt => "<", + CompareOp::Lte => "<=", + CompareOp::Eq => "==", + } +} + +fn write_indent(out: &mut String, indent: usize) { + for _ in 0..indent { + out.push(' '); + } +} + +fn write_quoted(out: &mut String, value: &str) { + out.push('"'); + for ch in value.chars() { + match ch { + '"' => out.push_str("\\\""), + '\\' => out.push_str("\\\\"), + '\n' => out.push_str("\\n"), + '\r' => out.push_str("\\r"), + '\t' => out.push_str("\\t"), + ch if ch.is_control() => { + let _ = write!(out, "\\u{:04x}", ch as u32); + } + ch => out.push(ch), + } + } + out.push('"'); +} + +fn reject_comments(source_id: SourceId, source: &str) -> Result<()> { + if let Some(offset) = comment_offset(source) { + return Err(Diagnostic::syntax( + Span::new(source_id, offset, offset + 1), + "formatter does not yet preserve comments", + )); + } + Ok(()) +} + +fn comment_offset(source: &str) -> Option { + let bytes = source.as_bytes(); + let mut index = 0; + while index < bytes.len() { + match bytes[index] { + b'"' => { + index += 1; + while index < bytes.len() { + match bytes[index] { + b'\\' => index += 2, + b'"' => { + index += 1; + break; + } + _ => index += 1, + } + } + } + b'#' => return Some(index), + _ => index += 1, + } + } + None +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::parse_source; + + #[test] + fn formats_top_level_fields() { + let formatted = format_source("port=Int&>=1 default 8080;name=String;").unwrap(); + assert_eq!( + formatted, + "port = Int & >= 1 default 8080;\nname = String;\n" + ); + parse_source(&formatted).unwrap(); + } + + #[test] + fn formats_nested_objects() { + let formatted = + format_source("Server={host=String default \"localhost\";port=Int&>0;};").unwrap(); + assert_eq!( + formatted, + "Server = {\n host = String default \"localhost\";\n port = Int & > 0;\n};\n" + ); + parse_source(&formatted).unwrap(); + } + + #[test] + fn formats_let_function_match_and_array() { + let source = "let f=(x:Int)=>match x{_: [1,{a=2;}];};in f(1)"; + let formatted = format_source(source).unwrap(); + assert_eq!( + formatted, + "let\n f = (x: Int) =>\n match x {\n _: [\n 1,\n {\n a = 2;\n },\n ];\n };\nin\n f(1)\n" + ); + parse_source(&formatted).unwrap(); + } + + #[test] + fn preserves_needed_parentheses() { + let formatted = format_source("value=(1+2)*3;").unwrap(); + assert_eq!(formatted, "value = (1 + 2) * 3;\n"); + parse_source(&formatted).unwrap(); + } + + #[test] + fn rejects_comments_instead_of_dropping_them() { + let error = format_source("# keep me\nvalue = 1;").unwrap_err(); + assert_eq!(error.message, "formatter does not yet preserve comments"); + } +} diff --git a/crates/decodal-core/src/lib.rs b/crates/decodal-core/src/lib.rs index 27900be..ce16e85 100644 --- a/crates/decodal-core/src/lib.rs +++ b/crates/decodal-core/src/lib.rs @@ -7,6 +7,7 @@ pub mod constraints; pub mod diagnostic; pub mod embedding; pub mod eval; +pub mod formatter; mod lexer; pub mod module; pub mod parser; @@ -21,6 +22,7 @@ pub use decodal_derive::Decodal; pub use diagnostic::{Diagnostic, DiagnosticKind, Result}; pub use embedding::{HostField, HostValue}; pub use eval::{Engine, format_diagnostic_with}; +pub use formatter::{format_source, format_source_with_source_id}; pub use module::{EmptyLoader, LoadedSource, Module, SourceLoader}; pub use parser::{ParseOutput, Parser, SourceForm, parse_source, parse_source_with_source_id}; pub use runtime::{Constraint, Data, ExprRef, LiteralValue, ModuleId, PrimitiveType, RuntimeValue}; diff --git a/crates/decodal-wasm/src/lib.rs b/crates/decodal-wasm/src/lib.rs index 1c5f7af..31df9ab 100644 --- a/crates/decodal-wasm/src/lib.rs +++ b/crates/decodal-wasm/src/lib.rs @@ -2,7 +2,7 @@ use std::collections::BTreeMap; use decodal::{ Data, Diagnostic, DiagnosticKind, EmptyLoader, Engine, LoadedSource, SourceId, SourceLoader, - Span, format_diagnostic_with, + Span, format_diagnostic_with, format_source, }; use wasm_bindgen::prelude::*; @@ -11,6 +11,17 @@ pub fn evaluate(source: &str) -> String { encode_result(evaluate_inner(source)) } +#[wasm_bindgen(js_name = formatSource)] +pub fn format_source_json(source: &str) -> String { + match format_source(source) { + Ok(formatted) => format!("{{\"ok\":true,\"source\":{}}}", json_string(&formatted)), + Err(error) => format!( + "{{\"ok\":false,\"error\":{}}}", + json_string(&format_diagnostic_with_root(&error, "playground")) + ), + } +} + #[wasm_bindgen(js_name = evaluateProject)] pub fn evaluate_project(entry: &str, files_json: &str) -> String { encode_result(evaluate_project_inner(entry, files_json)) @@ -214,6 +225,13 @@ fn json_string(value: &str) -> String { mod tests { use super::{evaluate_project_inner, normalize_path, resolve_import}; + #[test] + fn formats_source_for_web() { + let output = super::format_source_json("value={a=1;};"); + assert!(output.contains("\"ok\":true")); + assert!(output.contains("value = {\\n a = 1;\\n};\\n")); + } + #[test] fn normalizes_virtual_paths() { assert_eq!( diff --git a/doc/manual/souce/components.md b/doc/manual/souce/components.md index 83f50b3..c1f5a9c 100644 --- a/doc/manual/souce/components.md +++ b/doc/manual/souce/components.md @@ -8,7 +8,7 @@ The public surface is intentionally organized by use case: execute Decodal with ### Rust crate The `decodal` crate is the primary Rust runtime and embedding API. -It owns parsing, evaluation, materialization, diagnostics, host-provided values, and schema/decode traits. +It owns parsing, formatting, evaluation, materialization, diagnostics, host-provided values, and schema/decode traits. Rust applications should use this crate when they want to load Decodal source, evaluate it, or embed Decodal into a host program. Important paths: diff --git a/doc/manual/souce/formatting.md b/doc/manual/souce/formatting.md new file mode 100644 index 0000000..623e4ac --- /dev/null +++ b/doc/manual/souce/formatting.md @@ -0,0 +1,50 @@ +# Formatting + +Decodal includes an initial source formatter for parsed Decodal code. +The formatter is available from the Rust crate, the CLI, the WebAssembly package, and the playground. + +## CLI + +Print formatted source to stdout: + +```sh +decodal fmt config.dcdl +``` + +Format in place: + +```sh +decodal fmt --write config.dcdl +``` + +Read from stdin: + +```sh +cat config.dcdl | decodal fmt - +``` + +## Rust + +```rust +let formatted = decodal::format_source("Server={port=Int default 8080;};")?; +``` + +## WebAssembly + +```js +import init, { formatSource } from 'decodal-wasm'; + +await init(); +const result = JSON.parse(formatSource('Server={port=Int default 8080;};')); + +if (result.ok) { + console.log(result.source); +} else { + console.error(result.error); +} +``` + +## Current limitation + +The formatter currently rejects source containing line comments rather than silently dropping them. +Comment-preserving formatting requires attaching comments to syntax nodes or formatting from a concrete syntax tree, and should be implemented before comment-heavy source is formatted automatically. diff --git a/doc/manual/souce/index.md b/doc/manual/souce/index.md index b4a1323..d17bc4d 100644 --- a/doc/manual/souce/index.md +++ b/doc/manual/souce/index.md @@ -7,7 +7,8 @@ Decodal は Deferred Constraint Data Language、略称 DCDL のプロジェク 1. [Introduction](./introduction.md) 2. [Components](./components.md) -3. [Language Specification](./language/index.md) +3. [Formatting](./formatting.md) +4. [Language Specification](./language/index.md) 1. [Lexical Structure and Syntax](./language/syntax.md) 2. [Value](./language/value/index.md) 1. [String](./language/value/string.md) @@ -36,7 +37,7 @@ Decodal は Deferred Constraint Data Language、略称 DCDL のプロジェク 9. [Materialization and Errors](./language/materialization-and-errors.md) 10. [Naming Conventions](./language/naming.md) 11. [Examples](./language/examples.md) -4. [Implementation Design](./design/index.md) +5. [Implementation Design](./design/index.md) 1. [Execution Pipeline](./design/execution-pipeline.md) 2. [Runtime Model](./design/runtime-model.md) 3. [Thunk and Lazy Evaluation](./design/thunk-and-lazy-evaluation.md) @@ -44,5 +45,5 @@ Decodal は Deferred Constraint Data Language、略称 DCDL のプロジェク 5. [Diagnostics and Fallback](./design/diagnostics-and-fallback.md) 6. [Embedding API](./design/embedding-api.md) 7. [Features](./design/features.md) -5. [Development](./development.md) -6. [Open Issues](./open-issues.md) +6. [Development](./development.md) +7. [Open Issues](./open-issues.md) diff --git a/packages/decodal-wasm/README.md b/packages/decodal-wasm/README.md index 7a33125..72cb7b2 100644 --- a/packages/decodal-wasm/README.md +++ b/packages/decodal-wasm/README.md @@ -22,15 +22,15 @@ deno add jsr:@hare/decodal-wasm ## Usage ```js -import init, { evaluateProject } from 'decodal-wasm'; +import init, { evaluateProject, formatSource } from 'decodal-wasm'; await init(); -const result = evaluateProject(JSON.stringify({ - entry: 'main.dcdl', - files: { - 'main.dcdl': 'Server = { port = Int default 8080; };', - }, +const formatted = JSON.parse(formatSource('Server={port=Int default 8080;};')); +console.log(formatted.source); + +const result = evaluateProject('main.dcdl', JSON.stringify({ + 'main.dcdl': 'Server = { port = Int default 8080; };', })); console.log(JSON.parse(result)); diff --git a/packages/decodal-wasm/decodal_wasm.d.ts b/packages/decodal-wasm/decodal_wasm.d.ts index e88b72f..e56f72d 100644 --- a/packages/decodal-wasm/decodal_wasm.d.ts +++ b/packages/decodal-wasm/decodal_wasm.d.ts @@ -5,12 +5,15 @@ export function evaluate(source: string): string; export function evaluateProject(entry: string, files_json: string): string; +export function formatSource(source: string): string; + export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module; export interface InitOutput { readonly memory: WebAssembly.Memory; readonly evaluate: (a: number, b: number) => [number, number]; readonly evaluateProject: (a: number, b: number, c: number, d: number) => [number, number]; + readonly formatSource: (a: number, b: number) => [number, number]; readonly __wbindgen_externrefs: WebAssembly.Table; readonly __wbindgen_malloc: (a: number, b: number) => number; readonly __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number; diff --git a/packages/decodal-wasm/decodal_wasm.js b/packages/decodal-wasm/decodal_wasm.js index 800a76f..fe597bd 100644 --- a/packages/decodal-wasm/decodal_wasm.js +++ b/packages/decodal-wasm/decodal_wasm.js @@ -40,6 +40,25 @@ export function evaluateProject(entry, files_json) { wasm.__wbindgen_free(deferred3_0, deferred3_1, 1); } } + +/** + * @param {string} source + * @returns {string} + */ +export function formatSource(source) { + let deferred2_0; + let deferred2_1; + try { + const ptr0 = passStringToWasm0(source, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + const ret = wasm.formatSource(ptr0, len0); + deferred2_0 = ret[0]; + deferred2_1 = ret[1]; + return getStringFromWasm0(ret[0], ret[1]); + } finally { + wasm.__wbindgen_free(deferred2_0, deferred2_1, 1); + } +} function __wbg_get_imports() { const import0 = { __proto__: null, diff --git a/packages/decodal-wasm/decodal_wasm_bg.wasm b/packages/decodal-wasm/decodal_wasm_bg.wasm index c03a15e42ad19b02bb662f4ca27c9e2941dc7d44..5a6df24c0c60176116ac9729ac96665cba6ea800 100644 GIT binary patch delta 55735 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 delta 44297 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 [number, number]; export const evaluateProject: (a: number, b: number, c: number, d: number) => [number, number]; +export const formatSource: (a: number, b: number) => [number, number]; export const __wbindgen_externrefs: WebAssembly.Table; export const __wbindgen_malloc: (a: number, b: number) => number; export const __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number; diff --git a/site/decodal-site/scripts/prepare-wasm-package.mjs b/site/decodal-site/scripts/prepare-wasm-package.mjs index 250aae1..d7ffe10 100644 --- a/site/decodal-site/scripts/prepare-wasm-package.mjs +++ b/site/decodal-site/scripts/prepare-wasm-package.mjs @@ -34,15 +34,15 @@ deno add jsr:@hare/decodal-wasm ## Usage \`\`\`js -import init, { evaluateProject } from 'decodal-wasm'; +import init, { evaluateProject, formatSource } from 'decodal-wasm'; await init(); -const result = evaluateProject(JSON.stringify({ - entry: 'main.dcdl', - files: { - 'main.dcdl': 'Server = { port = Int default 8080; };', - }, +const formatted = JSON.parse(formatSource('Server={port=Int default 8080;};')); +console.log(formatted.source); + +const result = evaluateProject('main.dcdl', JSON.stringify({ + 'main.dcdl': 'Server = { port = Int default 8080; };', })); console.log(JSON.parse(result)); diff --git a/site/decodal-site/src/lib/docs.js b/site/decodal-site/src/lib/docs.js index ba791d9..6be6cd1 100644 --- a/site/decodal-site/src/lib/docs.js +++ b/site/decodal-site/src/lib/docs.js @@ -20,6 +20,7 @@ export const docs = Object.fromEntries( export const nav = [ { title: 'Introduction', slug: 'introduction' }, { title: 'Components', slug: 'components' }, + { title: 'Formatting', slug: 'formatting' }, { title: 'Language Specification', slug: 'language', diff --git a/site/decodal-site/src/pages/playground.astro b/site/decodal-site/src/pages/playground.astro index 09af8ab..1a179a5 100644 --- a/site/decodal-site/src/pages/playground.astro +++ b/site/decodal-site/src/pages/playground.astro @@ -15,6 +15,7 @@ import ManualLayout from '../layouts/ManualLayout.astro';

Loading WASM...

+ diff --git a/site/decodal-site/src/scripts/playground.js b/site/decodal-site/src/scripts/playground.js index 40c3447..a613b6b 100644 --- a/site/decodal-site/src/scripts/playground.js +++ b/site/decodal-site/src/scripts/playground.js @@ -1,4 +1,4 @@ -import init, { evaluateProject } from 'decodal-wasm'; +import init, { evaluateProject, formatSource } from 'decodal-wasm'; import { EditorView, basicSetup } from 'codemirror'; import { keymap } from '@codemirror/view'; import { decodal } from 'decodal-codemirror'; @@ -10,6 +10,7 @@ const starterProject = playgroundExamples[0]; const editorHost = document.getElementById('editor'); const output = document.getElementById('output'); const run = document.getElementById('run'); +const formatButton = document.getElementById('format'); const status = document.getElementById('status'); const fileTree = document.getElementById('file-tree'); const activeFile = document.getElementById('active-file'); @@ -176,6 +177,20 @@ function execute() { output.classList.toggle('error', !result.ok); } +function formatActiveFile() { + const result = JSON.parse(formatSource(getEditorText())); + if (!result.ok) { + output.textContent = result.error; + output.classList.add('error'); + return; + } + setEditorText(result.source); + project.files[project.activePath] = result.source; + saveProject(); + output.textContent = 'Formatted.'; + output.classList.remove('error'); +} + function renderFileTree() { const tree = buildTree(Object.keys(project.files).sort()); fileTree.replaceChildren(renderTreeList(tree.children)); @@ -229,6 +244,7 @@ function compareNodes(a, b) { try { await init(); run.disabled = false; + formatButton.disabled = false; status.textContent = ''; execute(); } catch (error) { @@ -236,6 +252,7 @@ try { } run.addEventListener('click', execute); +formatButton.addEventListener('click', formatActiveFile); loadExample.addEventListener('click', () => { const example = playgroundExamples.find((item) => item.id === exampleSelect.value); if (!example) return;