diff --git a/Cargo.lock b/Cargo.lock index 0b0e5ee..df3dc12 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -27,21 +27,21 @@ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" name = "decodal" version = "0.1.0" dependencies = [ - "decodal-core", + "regex", ] [[package]] -name = "decodal-core" +name = "decodal-cli" version = "0.1.0" dependencies = [ - "regex", + "decodal", ] [[package]] name = "decodal-wasm" version = "0.1.0" dependencies = [ - "decodal-core", + "decodal", "serde_json", "wasm-bindgen", ] diff --git a/README.md b/README.md index 251fa19..8db9ac1 100644 --- a/README.md +++ b/README.md @@ -2,40 +2,39 @@ Decodal is a small deterministic DSL for describing, composing, validating, and materializing structured data. -It is designed around a lightweight Rust core: +It is designed around a lightweight Rust library: - host-supplied imports through `SourceLoader` -- no filesystem access in `decodal-core` +- no filesystem access in the library core - concrete and abstract values with constraints and defaults - deterministic expression evaluation - optional regex support behind a Cargo feature - browser playground support through WebAssembly -## Install the CLI +## Library crate -```sh -cargo install decodal +Embedded hosts should depend on `decodal` and provide imports with a `SourceLoader`. + +```toml +[dependencies] +decodal = "0.1" ``` -Run a Decodal file: +## CLI + +A standalone CLI is kept in this repository as the `decodal-cli` workspace package. +It builds a `decodal` binary, but it is not the primary crates.io package. + +Run a Decodal file from the repository: ```sh -decodal examples/advanced/main.dcdl +cargo run -q -p decodal-cli -- examples/advanced/main.dcdl ``` Enable optional regex support when needed: ```sh -cargo install decodal --features regex -``` - -## Library crate - -Embedded hosts should depend on `decodal-core` and provide imports with a `SourceLoader`. - -```toml -[dependencies] -decodal-core = "0.1" +cargo run -q -p decodal-cli --features regex -- examples/regex/main.dcdl ``` ## Web playground diff --git a/crates/decodal-cli/Cargo.toml b/crates/decodal-cli/Cargo.toml index d63f66d..acbc3a2 100644 --- a/crates/decodal-cli/Cargo.toml +++ b/crates/decodal-cli/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "decodal" +name = "decodal-cli" version.workspace = true edition.workspace = true rust-version.workspace = true @@ -9,10 +9,15 @@ readme.workspace = true description = "Command-line interface for the Decodal data description language." keywords = ["decodal", "dsl", "config", "cli"] categories = ["command-line-utilities", "config"] +publish = false + +[[bin]] +name = "decodal" +path = "src/main.rs" [features] default = [] -regex = ["decodal-core/regex"] +regex = ["decodal/regex"] [dependencies] -decodal-core = { version = "0.1.0", path = "../decodal-core" } +decodal = { version = "0.1.0", path = "../decodal-core" } diff --git a/crates/decodal-cli/src/main.rs b/crates/decodal-cli/src/main.rs index 9809ba1..f040243 100644 --- a/crates/decodal-cli/src/main.rs +++ b/crates/decodal-cli/src/main.rs @@ -4,7 +4,7 @@ use std::{ process::ExitCode, }; -use decodal_core::{ +use decodal::{ Data, Diagnostic, DiagnosticKind, Engine, LoadedSource, SourceId, SourceLoader, Span, format_diagnostic_with, }; @@ -28,7 +28,7 @@ fn run() -> Result<(), String> { Ok(()) } "--version" | "-V" => { - println!("Decodal {}", decodal_core::version()); + println!("Decodal {}", decodal::version()); Ok(()) } "check" => { diff --git a/crates/decodal-core/Cargo.toml b/crates/decodal-core/Cargo.toml index 954f347..d300210 100644 --- a/crates/decodal-core/Cargo.toml +++ b/crates/decodal-core/Cargo.toml @@ -1,12 +1,12 @@ [package] -name = "decodal-core" +name = "decodal" version.workspace = true edition.workspace = true rust-version.workspace = true license.workspace = true repository.workspace = true readme.workspace = true -description = "Core parser, evaluator, and embedding API for the Decodal data description language." +description = "Parser, evaluator, and embedding API for the Decodal data description language." keywords = ["decodal", "dsl", "config", "schema"] categories = ["config", "parser-implementations"] diff --git a/crates/decodal-core/examples/host_prelude.rs b/crates/decodal-core/examples/host_prelude.rs index 640bcbe..803c90a 100644 --- a/crates/decodal-core/examples/host_prelude.rs +++ b/crates/decodal-core/examples/host_prelude.rs @@ -1,6 +1,6 @@ -use decodal_core::{Data, EmptyLoader, Engine, HostValue}; +use decodal::{Data, EmptyLoader, Engine, HostValue}; -fn main() -> decodal_core::Result<()> { +fn main() -> decodal::Result<()> { let mut engine = Engine::new(EmptyLoader); engine.bind_global( diff --git a/crates/decodal-wasm/Cargo.toml b/crates/decodal-wasm/Cargo.toml index d85ff3b..e227786 100644 --- a/crates/decodal-wasm/Cargo.toml +++ b/crates/decodal-wasm/Cargo.toml @@ -9,12 +9,13 @@ readme.workspace = true description = "WebAssembly wrapper for evaluating Decodal in browser playgrounds." keywords = ["decodal", "wasm", "dsl", "config"] categories = ["wasm", "config"] +publish = false [lib] crate-type = ["cdylib", "rlib"] [dependencies] -decodal-core = { version = "0.1.0", path = "../decodal-core" } +decodal = { version = "0.1.0", path = "../decodal-core" } serde_json.workspace = true wasm-bindgen.workspace = true diff --git a/crates/decodal-wasm/src/lib.rs b/crates/decodal-wasm/src/lib.rs index bf465d3..1c5f7af 100644 --- a/crates/decodal-wasm/src/lib.rs +++ b/crates/decodal-wasm/src/lib.rs @@ -1,6 +1,6 @@ use std::collections::BTreeMap; -use decodal_core::{ +use decodal::{ Data, Diagnostic, DiagnosticKind, EmptyLoader, Engine, LoadedSource, SourceId, SourceLoader, Span, format_diagnostic_with, }; @@ -81,7 +81,7 @@ impl SourceLoader for VirtualLoader { &mut self, current_key: Option<&str>, specifier: &str, - ) -> decodal_core::Result { + ) -> decodal::Result { let key = resolve_import(current_key, specifier).ok_or_else(|| { Diagnostic::new( DiagnosticKind::Import, @@ -139,7 +139,7 @@ fn normalize_path(path: &str) -> Option { } } -fn format_diagnostic_with_root(diagnostic: &decodal_core::Diagnostic, root_name: &str) -> String { +fn format_diagnostic_with_root(diagnostic: &decodal::Diagnostic, root_name: &str) -> String { format_diagnostic_with(diagnostic, |source| { (source == SourceId(0)).then_some(root_name) }) diff --git a/doc/manual/souce/design/embedding-api.md b/doc/manual/souce/design/embedding-api.md index 9d39e2f..4bfe70e 100644 --- a/doc/manual/souce/design/embedding-api.md +++ b/doc/manual/souce/design/embedding-api.md @@ -24,7 +24,7 @@ Primitive type names such as `String`, `Int`, `Float`, and `Bool` are handled be The host can bind values before adding or evaluating user sources. ```rust -use decodal_core::{EmptyLoader, Engine, HostValue}; +use decodal::{EmptyLoader, Engine, HostValue}; let mut engine = Engine::new(EmptyLoader); diff --git a/doc/manual/souce/design/features.md b/doc/manual/souce/design/features.md index ca38a4e..c8d5c06 100644 --- a/doc/manual/souce/design/features.md +++ b/doc/manual/souce/design/features.md @@ -37,7 +37,7 @@ primitive type conflict や明らかな numeric bound conflict は合成時に ## Core defaults -`decodal-core` defaults to `std` only. +`decodal` defaults to `std` only. ```toml [features] @@ -46,7 +46,7 @@ std = [] regex = ["std", "dep:regex"] ``` -Building `decodal-core` with `--no-default-features` keeps the core in `no_std + alloc` mode and avoids optional dependencies. +Building `decodal` with `--no-default-features` keeps the core in `no_std + alloc` mode and avoids optional dependencies. ## Regex @@ -54,7 +54,7 @@ Regex constraints are implemented behind the `regex` feature. When the feature is disabled, regex constraints parse and compose, but validating a concrete value against them returns an unsupported feature diagnostic. ```sh -cargo run -q -p decodal --features regex -- examples/regex/main.dcdl +cargo run -q -p decodal-cli --features regex -- examples/regex/main.dcdl ``` Regex constraints are accumulated during `&` composition. @@ -63,5 +63,5 @@ Concrete strings must match every regex constraint attached to the abstract valu ## CLI features -`decodal-cli` exposes a matching `regex` feature that enables `decodal-core/regex`. +`decodal-cli` exposes a matching `regex` feature that enables `decodal/regex`. The feature is not enabled by default so the default CLI binary remains small. diff --git a/doc/manual/souce/development.md b/doc/manual/souce/development.md index 33d954c..fa51c91 100644 --- a/doc/manual/souce/development.md +++ b/doc/manual/souce/development.md @@ -9,27 +9,25 @@ Run the normal Rust checks from the repository root. ```sh cargo fmt --check cargo test -cargo check -p decodal-core --no-default-features +cargo check -p decodal --no-default-features nix flake check ``` Regex support is optional and should be tested explicitly when touched. ```sh -cargo test -p decodal-core --features regex -cargo run -q -p decodal --features regex -- examples/regex/main.dcdl +cargo test -p decodal --features regex +cargo run -q -p decodal-cli --features regex -- examples/regex/main.dcdl ``` ## crates.io release -Crates are published as versioned workspace packages under the dual license `MIT OR Apache-2.0`. -The dependency order matters because `decodal` and `decodal-wasm` depend on `decodal-core` from crates.io. +The primary crates.io package is `decodal`, which contains the embeddable library. +Workspace support crates such as `decodal-cli` and `decodal-wasm` are not published for 0.1.0. +The project is dual licensed as `MIT OR Apache-2.0`. ```sh -cargo publish -p decodal-core -# wait until crates.io index has decodal-core 0.1.0 cargo publish -p decodal -cargo publish -p decodal-wasm ``` Before publishing, run: @@ -37,12 +35,10 @@ Before publishing, run: ```sh cargo fmt --check cargo test -cargo check -p decodal-core --no-default-features -cargo publish -p decodal-core --dry-run +cargo check -p decodal --no-default-features +cargo publish -p decodal --dry-run ``` -`cargo publish --dry-run` for `decodal` and `decodal-wasm` will fail until the matching `decodal-core` version is already visible in the crates.io index. - ## Web site and playground The Astro documentation site and browser playground are kept in: diff --git a/examples/advanced/README.md b/examples/advanced/README.md index 3b51fae..6a2fbc1 100644 --- a/examples/advanced/README.md +++ b/examples/advanced/README.md @@ -16,7 +16,7 @@ This example exercises multiple current Decodal features together: Run it with: ```sh -cargo run -q -p decodal -- examples/advanced/main.dcdl +cargo run -q -p decodal-cli -- examples/advanced/main.dcdl ``` The entrypoint is `main.dcdl`. diff --git a/examples/regex/README.md b/examples/regex/README.md index 933b4f5..752bfee 100644 --- a/examples/regex/README.md +++ b/examples/regex/README.md @@ -3,7 +3,7 @@ Regex constraints are optional. Run this example with the `regex` feature enabled: ```sh -cargo run -q -p decodal --features regex -- examples/regex/main.dcdl +cargo run -q -p decodal-cli --features regex -- examples/regex/main.dcdl ``` Without the feature, regex validation returns an unsupported feature diagnostic. diff --git a/package.nix b/package.nix index 0ddc6b6..8a19bc5 100644 --- a/package.nix +++ b/package.nix @@ -37,7 +37,7 @@ rustPlatform.buildRustPackage { cargoBuildFlags = [ "-p" - "decodal" + "decodal-cli" ]; doInstallCheck = true; diff --git a/site/decodal-site/src/wasm/README.md b/site/decodal-site/src/wasm/README.md new file mode 100644 index 0000000..8db9ac1 --- /dev/null +++ b/site/decodal-site/src/wasm/README.md @@ -0,0 +1,55 @@ +# Decodal + +Decodal is a small deterministic DSL for describing, composing, validating, and materializing structured data. + +It is designed around a lightweight Rust library: + +- host-supplied imports through `SourceLoader` +- no filesystem access in the library core +- concrete and abstract values with constraints and defaults +- deterministic expression evaluation +- optional regex support behind a Cargo feature +- browser playground support through WebAssembly + +## Library crate + +Embedded hosts should depend on `decodal` and provide imports with a `SourceLoader`. + +```toml +[dependencies] +decodal = "0.1" +``` + +## CLI + +A standalone CLI is kept in this repository as the `decodal-cli` workspace package. +It builds a `decodal` binary, but it is not the primary crates.io package. + +Run a Decodal file from the repository: + +```sh +cargo run -q -p decodal-cli -- examples/advanced/main.dcdl +``` + +Enable optional regex support when needed: + +```sh +cargo run -q -p decodal-cli --features regex -- examples/regex/main.dcdl +``` + +## Web playground + +The static documentation site and browser playground live under: + +```text +site/decodal-site/ +``` + +## License + +Licensed under either of: + +- Apache License, Version 2.0 +- MIT license + +at your option. diff --git a/site/decodal-site/src/wasm/decodal_wasm.d.ts b/site/decodal-site/src/wasm/decodal_wasm.d.ts index f6ffa5a..e88b72f 100644 --- a/site/decodal-site/src/wasm/decodal_wasm.d.ts +++ b/site/decodal-site/src/wasm/decodal_wasm.d.ts @@ -9,12 +9,13 @@ export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembl export interface InitOutput { readonly memory: WebAssembly.Memory; - readonly evaluate: (a: number, b: number, c: number) => void; - readonly evaluateProject: (a: number, b: number, c: number, d: number, e: number) => void; - readonly __wbindgen_add_to_stack_pointer: (a: number) => number; - readonly __wbindgen_export: (a: number, b: number) => number; - readonly __wbindgen_export2: (a: number, b: number, c: number, d: number) => number; - readonly __wbindgen_export3: (a: number, b: number, c: number) => void; + readonly evaluate: (a: number, b: number) => [number, number]; + readonly evaluateProject: (a: number, b: number, c: number, d: 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; + readonly __wbindgen_free: (a: number, b: number, c: number) => void; + readonly __wbindgen_start: () => void; } export type SyncInitInput = BufferSource | WebAssembly.Module; diff --git a/site/decodal-site/src/wasm/decodal_wasm.js b/site/decodal-site/src/wasm/decodal_wasm.js index 2a2a76e..800a76f 100644 --- a/site/decodal-site/src/wasm/decodal_wasm.js +++ b/site/decodal-site/src/wasm/decodal_wasm.js @@ -8,18 +8,14 @@ export function evaluate(source) { let deferred2_0; let deferred2_1; try { - const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); - const ptr0 = passStringToWasm0(source, wasm.__wbindgen_export, wasm.__wbindgen_export2); + const ptr0 = passStringToWasm0(source, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); const len0 = WASM_VECTOR_LEN; - wasm.evaluate(retptr, ptr0, len0); - var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true); - var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true); - deferred2_0 = r0; - deferred2_1 = r1; - return getStringFromWasm0(r0, r1); + const ret = wasm.evaluate(ptr0, len0); + deferred2_0 = ret[0]; + deferred2_1 = ret[1]; + return getStringFromWasm0(ret[0], ret[1]); } finally { - wasm.__wbindgen_add_to_stack_pointer(16); - wasm.__wbindgen_export3(deferred2_0, deferred2_1, 1); + wasm.__wbindgen_free(deferred2_0, deferred2_1, 1); } } @@ -32,25 +28,30 @@ export function evaluateProject(entry, files_json) { let deferred3_0; let deferred3_1; try { - const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); - const ptr0 = passStringToWasm0(entry, wasm.__wbindgen_export, wasm.__wbindgen_export2); + const ptr0 = passStringToWasm0(entry, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); const len0 = WASM_VECTOR_LEN; - const ptr1 = passStringToWasm0(files_json, wasm.__wbindgen_export, wasm.__wbindgen_export2); + const ptr1 = passStringToWasm0(files_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); const len1 = WASM_VECTOR_LEN; - wasm.evaluateProject(retptr, ptr0, len0, ptr1, len1); - var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true); - var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true); - deferred3_0 = r0; - deferred3_1 = r1; - return getStringFromWasm0(r0, r1); + const ret = wasm.evaluateProject(ptr0, len0, ptr1, len1); + deferred3_0 = ret[0]; + deferred3_1 = ret[1]; + return getStringFromWasm0(ret[0], ret[1]); } finally { - wasm.__wbindgen_add_to_stack_pointer(16); - wasm.__wbindgen_export3(deferred3_0, deferred3_1, 1); + wasm.__wbindgen_free(deferred3_0, deferred3_1, 1); } } function __wbg_get_imports() { const import0 = { __proto__: null, + __wbindgen_init_externref_table: function() { + const table = wasm.__wbindgen_externrefs; + const offset = table.grow(4); + table.set(0, undefined); + table.set(offset + 0, undefined); + table.set(offset + 1, null); + table.set(offset + 2, true); + table.set(offset + 3, false); + }, }; return { __proto__: null, @@ -58,14 +59,6 @@ function __wbg_get_imports() { }; } -let cachedDataViewMemory0 = null; -function getDataViewMemory0() { - if (cachedDataViewMemory0 === null || cachedDataViewMemory0.buffer.detached === true || (cachedDataViewMemory0.buffer.detached === undefined && cachedDataViewMemory0.buffer !== wasm.memory.buffer)) { - cachedDataViewMemory0 = new DataView(wasm.memory.buffer); - } - return cachedDataViewMemory0; -} - function getStringFromWasm0(ptr, len) { return decodeText(ptr >>> 0, len); } @@ -149,8 +142,8 @@ function __wbg_finalize_init(instance, module) { wasmInstance = instance; wasm = instance.exports; wasmModule = module; - cachedDataViewMemory0 = null; cachedUint8ArrayMemory0 = null; + wasm.__wbindgen_start(); return wasm; } diff --git a/site/decodal-site/src/wasm/decodal_wasm_bg.wasm b/site/decodal-site/src/wasm/decodal_wasm_bg.wasm index 7387400..377a9bc 100644 Binary files a/site/decodal-site/src/wasm/decodal_wasm_bg.wasm and b/site/decodal-site/src/wasm/decodal_wasm_bg.wasm differ diff --git a/site/decodal-site/src/wasm/decodal_wasm_bg.wasm.d.ts b/site/decodal-site/src/wasm/decodal_wasm_bg.wasm.d.ts index fac5baf..4f894cc 100644 --- a/site/decodal-site/src/wasm/decodal_wasm_bg.wasm.d.ts +++ b/site/decodal-site/src/wasm/decodal_wasm_bg.wasm.d.ts @@ -1,9 +1,10 @@ /* tslint:disable */ /* eslint-disable */ export const memory: WebAssembly.Memory; -export const evaluate: (a: number, b: number, c: number) => void; -export const evaluateProject: (a: number, b: number, c: number, d: number, e: number) => void; -export const __wbindgen_add_to_stack_pointer: (a: number) => number; -export const __wbindgen_export: (a: number, b: number) => number; -export const __wbindgen_export2: (a: number, b: number, c: number, d: number) => number; -export const __wbindgen_export3: (a: number, b: number, c: number) => void; +export const evaluate: (a: number, b: number) => [number, number]; +export const evaluateProject: (a: number, b: number, c: number, d: 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; +export const __wbindgen_free: (a: number, b: number, c: number) => void; +export const __wbindgen_start: () => void; diff --git a/site/decodal-site/src/wasm/package.json b/site/decodal-site/src/wasm/package.json index 7f7bce9..cf1987a 100644 --- a/site/decodal-site/src/wasm/package.json +++ b/site/decodal-site/src/wasm/package.json @@ -1,7 +1,13 @@ { "name": "decodal-wasm", "type": "module", + "description": "WebAssembly wrapper for evaluating Decodal in browser playgrounds.", "version": "0.1.0", + "license": "MIT OR Apache-2.0", + "repository": { + "type": "git", + "url": "https://gitea.hareworks.net/Hare/Decodal" + }, "files": [ "decodal_wasm_bg.wasm", "decodal_wasm.js", @@ -11,5 +17,11 @@ "types": "decodal_wasm.d.ts", "sideEffects": [ "./snippets/*" + ], + "keywords": [ + "decodal", + "wasm", + "dsl", + "config" ] }