Skip to content

Commit

Permalink
Improve code generation by hooking into SWIPL FLI
Browse files Browse the repository at this point in the history
SWIPL provides a FFI interface called the FLI that can make code
generation more ergonomic and less fallible. This PR uses that
interface. There are some rough edges in the library I used (cc
terminusdb-labs/swipl-rs#4), but it's still a lot better than generating
text and parsing SWIPL's output as text (which would probably not work
if an exception is raised).

There is still a bit more that needs to be done in terms of ergonomics
and error handling, but this prototype is at a pretty good state at the
moment.

This commit is a squash of the following commits:
- [WIP] Improved codegen
- Revamp prelude/builtins, move save state to target/
- Remove unnecessary primitive, unnecessary dependencies, and fix clippy
  warnings
- Undo some parser/deps changes
- Revert the previous
- Remove merge artifact
- Fix code for new AST

Co-authored-by: Caden Haustein <code@brightlysalty.33mail.com>
  • Loading branch information
ThePuzzlemaker and brightly-salty committed Oct 5, 2021
1 parent b7f1cef commit f0ac83f
Show file tree
Hide file tree
Showing 11 changed files with 427 additions and 158 deletions.
6 changes: 5 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,23 @@ keywords = ["cli", "parser", "langdev", "language"]
categories = ["command-line-utilities", "compilers", "text-editors"]
license-file = "LICENSE.md"
include = ["src/**/*", "LICENSE.md"]
build = "build.rs"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
anyhow = "1.0.44"
combine = "4.6.1"
termion = "1.5.6"
unic-ucd-category = "0.9.0"
swipl = "0.3.5"

[dependencies.async-std]
version = "1.10.0"
features = ["unstable", "attributes"]

[build-dependencies]
swipl-info = "0.3.1"

[features]

[profile.release]
Expand Down
23 changes: 23 additions & 0 deletions build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
use std::env;
use std::path::Path;
use std::process::Command;

use swipl_info::get_swipl_info;

fn main() {
println!("cargo:rerun-if-changed=build.rs");
println!("cargo:rerun-if-changed=mlatu.pl");
println!("cargo:rustc-link-arg=-Wl,-rpath,{}", get_swipl_info().lib_dir);

let out_dir = env::var_os("OUT_DIR").unwrap();
let dest_path = Path::new(&out_dir).join("mlatu.pl.save");
Command::new("swipl").arg("-o")
.arg(dest_path)
.arg("--goal=true")
.arg("-c")
.arg("mlatu.pl")
.spawn()
.expect("spawn")
.wait()
.expect("wait");
}
37 changes: 37 additions & 0 deletions mlatu.pl
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
:- module(mlatu, [rewrite/2, user_equiv/2]).
:- dynamic([user_equiv/2], [multifile, discontiguous]).

equiv(Xs, Other) :-
user_equiv(Xs, Other) ;
builtin_equiv(Xs, Other).

builtin_equiv(['d',X|Xs], Other) :-
equiv([X,X|Xs], Other).

builtin_equiv(['r',_|Xs], Other) :-
equiv(Xs, Other).

builtin_equiv(['u',Quote|Xs], Other) :-
append(Quote, Xs, NewXs),
equiv(NewXs, Other).

builtin_equiv(['q',X|Xs], Other) :-
equiv([[X]|Xs], Other).

builtin_equiv(['c',Quote1,Quote2|Xs], Other) :-
append(Quote1, Quote2, NewQuote),
equiv([NewQuote|Xs], Other).

builtin_equiv(['s',A,B|Xs], Other) :-
equiv([B,A|Xs],Other).

builtin_equiv([], []).
builtin_equiv([X|Xs], Other) :-
equiv(Xs, New),
(Xs \= New ->
equiv([X|New], Other) ;
Other = [X|Xs]).

rewrite(List, Other) :-
equiv(List, Other),
List \= Other.
14 changes: 12 additions & 2 deletions prelude.mlt
Original file line number Diff line number Diff line change
@@ -1,2 +1,12 @@
q = ( ) const;
swap = q dip;
concat = c;
unquote = u;
quote = q;
swap = s;
remove = r;
dup = d;

prepend = swap quote swap concat;
p = prepend;

unquote-before = swap quote cat unquote;
uqb = unquote-before;
57 changes: 0 additions & 57 deletions src/execute.rs

This file was deleted.

78 changes: 0 additions & 78 deletions src/gen.rs

This file was deleted.

5 changes: 1 addition & 4 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,10 @@

mod ast;
// mod editor;
mod execute;
mod gen;
mod parser;
pub mod prolog;
// mod terminal;

pub use ast::{pretty_rule, Rule, Term};
// pub use editor::Editor;
pub use execute::execute;
pub use gen::generate;
pub use parser::{parse_rule, parse_rules, parse_term, parse_terms};
106 changes: 90 additions & 16 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,96 @@
use async_std::fs::OpenOptions;
use async_std::io;
use async_std::io::prelude::*;
use async_std::path::PathBuf;
use async_std::prelude::*;
use mlatu::parse_rules;
use mlatu::{parse_rules, parse_terms, prolog};
use prolog::util::AssertLocation;
use prolog::{codegen, ContextExt};

// TODO: actual CLI, better REPL (w/ rustyline)
// TODO: Bring back structural editing
// BUG: Ctrl-C forwards to SWIPl
#[async_std::main]
async fn main() -> std::io::Result<()> {
// let path = if let Some(filename) = std::env::args().nth(2) {
// PathBuf::from(filename)
// } else {
// PathBuf::from("./prelude.mlt")
// };
// let mut file =
// OpenOptions::new().read(true).write(true).create(true).open(path).await?;
// let mut s = String::new();
// let _ = file.read_to_string(&mut s).await?;
// match parse_rules(&s) {
// | Ok(rules) => Editor::new(file, rules).unwrap().run().await,
// | Err(error) => eprintln!("{}", error),
// }
Ok(())
let engine = mlatu::prolog::init_engine();
let ctx:prolog::Context<'_, _> = engine.activate().into();

let path = if let Some(filename) = std::env::args().nth(2) {
PathBuf::from(filename)
} else {
PathBuf::from("./prelude.mlt")
};
let mut file = OpenOptions::new().read(true).write(true).create(true).open(path).await?;
let mut s = String::new();
let _ = file.read_to_string(&mut s).await?;
let rules = match parse_rules(&s) {
| Ok(rules) => rules,
| Err(error) => {
eprintln!("{}", error);
return Ok(())
},
};

let module = prolog::Module::new("mlatu");

let clauses = match codegen::generate(&ctx, &rules) {
| Ok(clauses) => clauses,
| Err(error) => {
eprintln!("Error while compiling rules: {}", error);
return Ok(())
},
};

if let Err(error) =
clauses.into_iter()
.try_for_each(|clause| ctx.assert(&clause.clause, Some(&module), AssertLocation::Last))
{
eprintln!("Error while compiling rules: {}", error);
return Ok(())
};

let stdin = io::stdin();
let mut stdout = io::stdout();

loop {
let mut contents = String::new();
print!(">>> ");
stdout.flush().await?;
stdin.read_line(&mut contents).await?;

if contents.trim() == "exit" || contents.trim() == "quit" || contents.is_empty() {
println!("Goodbye!");
break Ok(())
}

let terms = match parse_terms(&contents) {
| Ok(terms) => terms,
| Err(error) => {
eprintln!("{}", error);
continue
},
};

let (list, other) = match codegen::generate_query(&ctx, &terms) {
| Ok(q) => q,
| Err(error) => {
eprintln!("Error while compiling query: {}", error);
continue
},
};

if let Err(error) = ctx.call_once(prolog::pred!(mlatu: rewrite / 2), [&list, &other]) {
eprintln!("Error while executing query: {}", error);
continue
}
// TODO: proper printing
let canon = match ctx.canonical(&other) {
| Some(s) => s,
| None => {
eprintln!("Could not format output");
continue
},
};

println!("==> {}", canon);
}
}
22 changes: 22 additions & 0 deletions src/prolog.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
pub use swipl::fli;
pub use swipl::prelude::*;

pub mod codegen;
pub mod util;
pub use util::ContextExt;

static SAVED_STATE:&[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/mlatu.pl.save"));

#[must_use]
pub fn init_engine() -> Engine {
let bytes = SAVED_STATE.as_ptr();
// SAFETY: This is called *before* `PL_initialise` (called when creating an
// engine). This data is also valid and the length is correct, as we take the
// pointer and length from a known valid slice.
let len = u64::try_from(SAVED_STATE.len()).expect("length");
unsafe {
fli::PL_set_resource_db_mem(bytes, len);
}

Engine::new()
}
Loading

0 comments on commit f0ac83f

Please sign in to comment.