Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Debug button to show Datalog AST #16

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,5 @@ pkg/
/mocha-setup.js
/test-loader.js
/test.html

/crates/percival/bindings/
77 changes: 77 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 18 additions & 3 deletions crates/percival-cli/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
//! Crate containing code for the `percival-cli` binary.

use std::error::Error;
use std::{
fs::read_to_string,
io::{self, Read, Write},
path::PathBuf,
process::{self, Command, Stdio},
};

use clap::Parser;
use clap::{ArgEnum, Parser};

use percival::{codegen::compile, errors::format_errors, parser::Grammar};
use percival::{codegen::compile as compile_js, errors::format_errors, parser::Grammar};

#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ArgEnum, Debug)]
enum Emitter {
JS,
Json,
}

/// Convenience CLI for testing the Percival language compiler.
#[derive(Parser, Debug)]
Expand All @@ -22,6 +29,9 @@ struct Opt {
/// Runs prettier and bat on the output.
#[clap(short, long)]
format: bool,

#[clap(short, long, arg_enum, default_value_t = Emitter::JS)]
emit: Emitter,
}

/// Run the main program.
Expand Down Expand Up @@ -49,7 +59,12 @@ fn main() {
}
};

match compile(&prog) {
let emitted: Result<String, Box<dyn Error>> = match opt.emit {
Emitter::JS => compile_js(&prog).map_err(|err| err.into()),
Emitter::Json => prog.json().map_err(|err| err.into()),
};

match emitted {
Ok(js) => {
if !opt.format {
println!("{}", js);
Expand Down
2 changes: 1 addition & 1 deletion crates/percival-wasm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ default = ["console_error_panic_hook"]
[dependencies]
console_error_panic_hook = { version = "0.1", optional = true }
percival = { path = "../percival" }
wasm-bindgen = "0.2"
wasm-bindgen = { version = "0.2", features = ["serde-serialize"] }
yansi = "0.5.0"

[dev-dependencies]
Expand Down
16 changes: 16 additions & 0 deletions crates/percival-wasm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,22 @@ impl CompilerResult {
self.0.as_ref().ok().map(|(_, js)| js.clone())
}

/// Returns the AST of the program's source.
pub fn ast(&self) -> JsValue {
self.0
.as_ref()
.ok()
.map(|(prog, _)| match JsValue::from_serde(prog) {
Ok(ast) => ast,
Err(err) => {
// XX: wasm-bindgen claims to have errors, but doesn't
eprintln!("{} {}", Paint::red("Error:"), err);
JsValue::UNDEFINED
}
})
.unwrap_or(JsValue::UNDEFINED)
}

/// Returns the names of relations that are dependencies of this program.
pub fn deps(&self) -> Option<Vec<JsValue>> {
self.0.as_ref().ok().map(|(prog, _)| {
Expand Down
3 changes: 3 additions & 0 deletions crates/percival/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ ariadne = "0.1.3"
chumsky = "0.7.0"
rpds = "0.11.0"
thiserror = "1.0.30"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
ts-rs = "6.1"

[dev-dependencies]
maplit = "1.0.2"
44 changes: 36 additions & 8 deletions crates/percival/src/ast.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,40 @@
//! Abstract syntax tree definitions for the Percival language.

// How to build AST types:
//
// cargo test ast::export_bindings
// mkdir -p $(percival_wasm_pkg)/ast
// for ts in $(percival_bindings)/* ; do \
// cp $$ts $(percival_wasm_pkg)/ast/"$$(basename "$${ts%.ts}.d.ts")" ; \
// done
// sed -i '' -e 's~ast(): any~ast(): import("./ast/Program").Program | undefined~' $(percival_wasm_pkg)/percival_wasm.d.ts
//

use serde::Serialize;
use serde_json::{to_string_pretty, Result as JsonResult};
use std::collections::{BTreeMap, BTreeSet};
use ts_rs::TS;

/// A program translation unit in the Percival language.
#[derive(Clone, Debug, PartialEq, Eq)]
#[derive(Clone, Debug, PartialEq, Eq, Serialize, TS)]
#[ts(export)]
pub struct Program {
/// Rules that make up the program.
pub rules: Vec<Rule>,
/// Imports prefixed with the `import` keyword.
pub imports: Vec<Import>,
}

impl Program {
/// Convert the program to a JSON string.
pub fn json(&self) -> JsonResult<String> {
to_string_pretty(self)
}
}

/// Represents a single Horn clause.
#[derive(Clone, Debug, PartialEq, Eq)]
#[derive(Clone, Debug, PartialEq, Eq, Serialize, TS)]
#[ts(export)]
pub struct Rule {
/// Head or implicand of the Horn clause.
pub goal: Fact,
Expand All @@ -21,7 +43,8 @@ pub struct Rule {
}

/// An element of the right-hand side of a rule.
#[derive(Clone, Debug, PartialEq, Eq)]
#[derive(Clone, Debug, PartialEq, Eq, Serialize, TS)]
#[ts(export)]
pub enum Clause {
/// Relational assumption in the rule.
Fact(Fact),
Expand All @@ -32,7 +55,8 @@ pub enum Clause {
}

/// Literal part of a Horn clause, written in terms of relations.
#[derive(Clone, Debug, PartialEq, Eq)]
#[derive(Clone, Debug, PartialEq, Eq, Serialize, TS)]
#[ts(export)]
pub struct Fact {
/// Name of the relation being referenced.
pub name: String,
Expand All @@ -41,7 +65,8 @@ pub struct Fact {
}

/// A bound or unbound value assigned to part of a relation.
#[derive(Clone, Debug, PartialEq, Eq)]
#[derive(Clone, Debug, PartialEq, Eq, Serialize, TS)]
#[ts(export)]
pub enum Value {
/// A simple identifier, which can be either bound or unbound.
Id(String),
Expand All @@ -54,7 +79,8 @@ pub enum Value {
}

/// Literal values supported by the Percival grammar.
#[derive(Clone, Debug, PartialEq, Eq)]
#[derive(Clone, Debug, PartialEq, Eq, Serialize, TS)]
#[ts(export)]
pub enum Literal {
/// A standard floating-point number literal.
Number(String),
Expand All @@ -65,7 +91,8 @@ pub enum Literal {
}

/// An aggregate operation over stratified dependency relations.
#[derive(Clone, Debug, PartialEq, Eq)]
#[derive(Clone, Debug, PartialEq, Eq, Serialize, TS)]
#[ts(export)]
pub struct Aggregate {
/// Name of the aggregate operator, such as `min` or `sum`.
pub operator: String,
Expand All @@ -76,7 +103,8 @@ pub struct Aggregate {
}

/// An external import from a static JSON dataset.
#[derive(Clone, Debug, PartialEq, Eq)]
#[derive(Clone, Debug, PartialEq, Eq, Serialize, TS)]
#[ts(export)]
pub struct Import {
/// Name of the relation being imported.
pub name: String,
Expand Down
12 changes: 12 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
"remark-rehype": "^10.1.0",
"svelte": "^3.46.6",
"svelte-icons": "^2.1.0",
"svelte-json-tree": "^1.0.0",
"unified": "^10.1.2"
},
"devDependencies": {
Expand Down
Loading