Compare commits
9 Commits
d836a66e66
...
partially_
| Author | SHA1 | Date | |
|---|---|---|---|
| 92bcb6f358 | |||
| 48de9517c9 | |||
| 9162b456d2 | |||
| 1df3acccd7 | |||
| 7b334e96df | |||
| 04bf1c6249 | |||
| d4920ec4b6 | |||
| 865fc1b4b6 | |||
| 46e6e27f88 |
@@ -13,7 +13,7 @@ with partial evaluation during compilation to make it efficient.
|
||||
|
||||
koka_bench: Licensed under Apache-2.0, as they are derived from the benchmarks of the Koka project, see the readme and license in koka_bench for more, or https://github.com/koka-lang/koka for the source project.
|
||||
|
||||
Kraken (everything else besides the benchmarks in koka_bench) Licensed under
|
||||
Kraken (everything else besides the benchmarks in koka_bench, and Recursive/CodeJar/Highlight.js in website/) Licensed under
|
||||
SPDX-License-Identifier: BSD-2-Clause-Patent
|
||||
|
||||
Note: This license is designed to provide: a) a simple permissive license; b) that is compatible with the GNU General Public License (GPL), version 2; and c) which also has an express patent grant included.
|
||||
|
||||
76
basic_dyns
76
basic_dyns
@@ -3,10 +3,82 @@
|
||||
|
||||
|
||||
|
||||
RB-TREE
|
||||
|
||||
evals all_evals evaled_wrap_1 evaled_wrap_0 compiled_dyn_1 compiled_dyn_0
|
||||
non-PE 114_170 5_032_297 1_291_489 398_104 1 0
|
||||
PE 0 0 0 0 10 0
|
||||
non-PE10 114_170 5_032_297 1_291_489 398_104 1 0
|
||||
PE10 0 0 0 0 10 0
|
||||
nonPE9 97087 4278001 1097965 338383 1 0
|
||||
PE9 0 0 0 0 9 0
|
||||
nonPE8 80160 3532131 906548 279379 1 0
|
||||
PE8 0 0 0 0 8 0
|
||||
nonPE7 67050 2952848 757932 233513 1 0
|
||||
PE7 0 0 0 0 7 0
|
||||
nonPE6 50361 2219792 569723 175593 1 0
|
||||
PE6 0 0 0 0 6 0
|
||||
nonPE5 37299 1643049 421745 129938 1 0
|
||||
PE5 0 0 0 0 5 0
|
||||
nonPE4 24393 1074730 275873 85000 1 0
|
||||
PE4 0 0 0 0 4 0
|
||||
nonPE3 15304 672998 172802 53200 1 0
|
||||
PE3 0 0 0 0 3 0
|
||||
nonPE2 6453 284080 72939 22484 1 0
|
||||
PE2 0 0 0 0 2 0
|
||||
nonPE1 1385 59899 15413 4750 1 0
|
||||
PE1 0 0 0 0 1 0
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Fib
|
||||
|
||||
evals all_evals evaled_wrap_1 evaled_wrap_0 compiled_dyn_1 compiled_dyn_0
|
||||
non-PE35 29860726 1320920726 337695363 119442881 1 0
|
||||
non-PE30 2692560 119107888 30450112 10770217 1 0
|
||||
non-PE25 242808 10740492 2745825 971209 1 0
|
||||
non-PE20 21914 969010 247731 87633 1 0
|
||||
non-PE15 1996 87916 22478 7961 1 0
|
||||
non-PE10 200 8468 2167 777 1 0
|
||||
|
||||
PE10 0 0 0 0 0 0
|
||||
PE15 0 0 0 0 0 0
|
||||
PE20 0 0 0 0 0 0
|
||||
PE25 0 0 0 0 0 0
|
||||
PE30 0 0 0 0 0 0
|
||||
PE35 0 0 0 0 0 0
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cfold
|
||||
evals all_evals evaled_wrap_1 evaled_wrap_0 compiled_dyn_1 compiled_dyn_0
|
||||
non-PE5 239660 10897376 2784275 879066 1 0
|
||||
PE5 0 0 0 0 0 0
|
||||
|
||||
|
||||
deriv
|
||||
evals all_evals evaled_wrap_1 evaled_wrap_0 compiled_dyn_1 compiled_dyn_0
|
||||
non-PE2 257693 11708558 2990090 946500 1 0
|
||||
PE2 0 0 0 0 2 0
|
||||
|
||||
|
||||
nqueens
|
||||
evals all_evals evaled_wrap_1 evaled_wrap_0 compiled_dyn_1 compiled_dyn_0
|
||||
non-PE7 271720 13530241 3429161 1108393 1 0
|
||||
PE7 0 0 0 0 0 0
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
18
flake.lock
generated
18
flake.lock
generated
@@ -36,11 +36,11 @@
|
||||
"nixpkgs": "nixpkgs"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1676341851,
|
||||
"narHash": "sha256-T8cmSiriXdpZfqlserNyJ1solTCR0DbD8A75epSDOVY=",
|
||||
"lastModified": 1678760344,
|
||||
"narHash": "sha256-N8u9/O0NWt3PUQc9xmCeod1SFilOFicALjtYtslib2g=",
|
||||
"owner": "oxalica",
|
||||
"repo": "rust-overlay",
|
||||
"rev": "956ddb5047f98ea08b792b22004b94a9971932c4",
|
||||
"rev": "d907affef544f64bd6886fe6bcc5fa2495a82373",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -67,11 +67,11 @@
|
||||
},
|
||||
"nixpkgs_stable_new": {
|
||||
"locked": {
|
||||
"lastModified": 1676177817,
|
||||
"narHash": "sha256-OQnBnuKkpwkfNY31xQyfU5hNpLs1ilWt+hVY6ztEEOM=",
|
||||
"lastModified": 1678703398,
|
||||
"narHash": "sha256-Y1mW3dBsoWLHpYm+UIHb5VZ7rx024NNHaF16oZBx++o=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "1b82144edfcd0c86486d2e07c7298f85510e7fb8",
|
||||
"rev": "67f26c1cfc5d5783628231e776a81c1ade623e0b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -97,11 +97,11 @@
|
||||
},
|
||||
"nixpkgs_unstable": {
|
||||
"locked": {
|
||||
"lastModified": 1676342081,
|
||||
"narHash": "sha256-zpHbXgvUYTJ9r1WgKtwhj/dmVthZ/GlW1oBYOdqJ9yg=",
|
||||
"lastModified": 1678838343,
|
||||
"narHash": "sha256-aA48yVAUyppdlVHhMStlWjB8u9uzA5iel3C47xlbkrw=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "4106c7519bff1d14fa5f942da645b3f18d16309e",
|
||||
"rev": "b16f2a75619fe8e6adf4583f5fc6448bc967d482",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
||||
7
kr/Cargo.lock
generated
7
kr/Cargo.lock
generated
@@ -11,6 +11,12 @@ dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.70"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4"
|
||||
|
||||
[[package]]
|
||||
name = "ascii-canvas"
|
||||
version = "3.0.0"
|
||||
@@ -167,6 +173,7 @@ dependencies = [
|
||||
name = "kr"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"lalrpop",
|
||||
"lalrpop-util",
|
||||
"once_cell",
|
||||
|
||||
@@ -10,6 +10,7 @@ build = "build.rs"
|
||||
lalrpop-util = {version="0.19.7", features=["lexer"]}
|
||||
regex = "1"
|
||||
once_cell = "1.17.0"
|
||||
anyhow = "1"
|
||||
|
||||
[build-dependencies]
|
||||
lalrpop = "0.19.7"
|
||||
|
||||
1029
kr/src/ast.rs
1029
kr/src/ast.rs
File diff suppressed because it is too large
Load Diff
620
kr/src/main.rs
620
kr/src/main.rs
@@ -4,7 +4,11 @@ lalrpop_mod!(pub grammar);
|
||||
use std::rc::Rc;
|
||||
|
||||
mod ast;
|
||||
use crate::ast::{mark,partial_eval,new_base_ctxs,eval,root_env,MarkedForm,Form,PossibleTailCall};
|
||||
use crate::ast::{eval,root_env};
|
||||
mod pe_ast;
|
||||
use crate::pe_ast::{mark,partial_eval,new_base_ctxs};
|
||||
|
||||
mod test;
|
||||
|
||||
|
||||
fn main() {
|
||||
@@ -15,620 +19,8 @@ fn main() {
|
||||
let (bctx, marked) = mark(Rc::clone(&parsed_input),bctx);
|
||||
let unvaled = marked.unval().unwrap();
|
||||
println!("Parsed unvaled that is {}", unvaled);
|
||||
let (bctx, ped) = partial_eval(bctx, dctx, unvaled);
|
||||
let (bctx, ped) = partial_eval(bctx, dctx, unvaled).unwrap();
|
||||
let result = eval(root_env(), parsed_input);
|
||||
println!("Result is {} - {:?}", result, result);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_test() {
|
||||
let g = grammar::TermParser::new();
|
||||
for test in [
|
||||
"22", "(22)", "(((22)))",
|
||||
"(22 )", "()", "( )", "( 44)", "(44 )",
|
||||
"(22 44 (1) 33 (4 5 (6) 6))", "hello",
|
||||
"-", "+", "(+ 1 ;hi
|
||||
3)", "'13", "hello-world", "_",
|
||||
] {
|
||||
assert!(g.parse(test).is_ok());
|
||||
}
|
||||
assert!(g.parse("((22)").is_err());
|
||||
}
|
||||
|
||||
fn eval_test<T: Into<Form>>(also_pe: bool, gram: &grammar::TermParser, e: &Rc<Form>, code: &str, expected: T) {
|
||||
println!("Doing test {}", code);
|
||||
let parsed = Rc::new(gram.parse(code).unwrap());
|
||||
let basic_result = eval(Rc::clone(e), Rc::clone(&parsed));
|
||||
assert_eq!(*basic_result, expected.into());
|
||||
if also_pe {
|
||||
let (bctx, dctx) = new_base_ctxs();
|
||||
let (bctx, marked) = mark(parsed,bctx);
|
||||
let unvaled = marked.unval().unwrap();
|
||||
let (bctx, ped) = partial_eval(bctx, dctx, unvaled);
|
||||
let (bctx, marked_basic_result) = mark(basic_result,bctx);
|
||||
println!("Final PE {}", ped);
|
||||
println!("wanted {}", marked_basic_result);
|
||||
assert_eq!(*ped, *marked_basic_result);
|
||||
}
|
||||
}
|
||||
fn partial_eval_test(gram: &grammar::TermParser, code: &str, expected: &str) {
|
||||
println!("Doing PE test {}", code);
|
||||
let parsed = Rc::new(gram.parse(code).unwrap());
|
||||
let (bctx, dctx) = new_base_ctxs();
|
||||
let (bctx, marked) = mark(parsed,bctx);
|
||||
let unvaled = marked.unval().unwrap();
|
||||
let (bctx, ped) = partial_eval(bctx, dctx, unvaled);
|
||||
println!("Final PE {}", ped);
|
||||
println!("wanted {}", expected);
|
||||
assert_eq!(format!("{}", ped), expected);
|
||||
}
|
||||
#[test]
|
||||
fn basic_pe_test() {
|
||||
let g = grammar::TermParser::new();
|
||||
partial_eval_test(&g, "(+ 2 (car (cons 4 '(1 2))))", "6");
|
||||
partial_eval_test(&g, "(vau 0 p (+ 1 2))", "None({})#[None/None/EnvID(0)/0/[]/Some(\"p\")/3]");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn basic_eval_test() { let g = grammar::TermParser::new(); let e = root_env();
|
||||
eval_test(true, &g, &e, "(+ 2 (car (cons 4 '(1 2))))", 6);
|
||||
eval_test(true, &g, &e, "(= 17 ((vau d p (+ (eval (car p) d) 13)) (+ 1 3)))", true);
|
||||
eval_test(true, &g, &e, "(if (= 2 2) (+ 1 2) (+ 3 4))", 3);
|
||||
eval_test(true, &g, &e, "(quote a)", "a");
|
||||
eval_test(true, &g, &e, "'a", "a");
|
||||
eval_test(true, &g, &e, "'(1 . a)", (1, "a"));
|
||||
eval_test(true, &g, &e, "'(1 a)", (1, ("a", Form::Nil)));
|
||||
eval_test(true, &g, &e, "true", true);
|
||||
eval_test(true, &g, &e, "false", false);
|
||||
eval_test(true, &g, &e, "nil", Form::Nil);
|
||||
|
||||
eval_test(true, &g, &e, "(+ 1 2)", 3);
|
||||
eval_test(true, &g, &e, "(- 1 2)", -1);
|
||||
eval_test(true, &g, &e, "(* 1 2)", 2);
|
||||
eval_test(true, &g, &e, "(/ 4 2)", 2);
|
||||
eval_test(true, &g, &e, "(% 3 2)", 1);
|
||||
eval_test(true, &g, &e, "(& 3 2)", 2);
|
||||
eval_test(true, &g, &e, "(| 2 1)", 3);
|
||||
eval_test(true, &g, &e, "(^ 2 1)", 3);
|
||||
eval_test(true, &g, &e, "(^ 3 1)", 2);
|
||||
|
||||
eval_test(true, &g, &e, "(< 3 1)", false);
|
||||
eval_test(true, &g, &e, "(<= 3 1)", false);
|
||||
eval_test(true, &g, &e, "(> 3 1)", true);
|
||||
eval_test(true, &g, &e, "(>= 3 1)", true);
|
||||
|
||||
eval_test(true, &g, &e, "(comb? +)", true);
|
||||
eval_test(true, &g, &e, "(comb? (vau d p 1))", true);
|
||||
eval_test(true, &g, &e, "(comb? 1)", false);
|
||||
eval_test(true, &g, &e, "(pair? '(a))", true);
|
||||
//eval_test(true, &g, &e, "(pair? '())", true);
|
||||
eval_test(true, &g, &e, "(nil? nil)", true);
|
||||
eval_test(true, &g, &e, "(nil? 1)", false);
|
||||
eval_test(true, &g, &e, "(pair? 1)", false);
|
||||
eval_test(true, &g, &e, "(symbol? 'a)", true);
|
||||
eval_test(true, &g, &e, "(symbol? 1)", false);
|
||||
eval_test(true, &g, &e, "(int? 1)", true);
|
||||
eval_test(true, &g, &e, "(int? true)", false);
|
||||
eval_test(true, &g, &e, "(bool? true)", true);
|
||||
eval_test(true, &g, &e, "(bool? 1)", false);
|
||||
|
||||
eval_test(true, &g, &e, "!(bool?) 1", false);
|
||||
eval_test(true, &g, &e, "!(bool?) true", true);
|
||||
|
||||
eval_test(true, &g, &e, "((vau root_env _ (eval 'a (cons (cons 'a 2) root_env))))", 2);
|
||||
eval_test(true, &g, &e, "'name-dash", "name-dash");
|
||||
}
|
||||
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
static LET: Lazy<String> = Lazy::new(|| {
|
||||
"!((vau root_env p (eval (car p)
|
||||
(cons (cons 'let1
|
||||
(vau de p (eval (car (cdr (cdr p))) (cons (cons (car p) (eval (car (cdr p)) de)) de)))
|
||||
) root_env))))".to_owned()
|
||||
});
|
||||
|
||||
#[test]
|
||||
fn let_pe_test() {
|
||||
let g = grammar::TermParser::new();
|
||||
partial_eval_test(&g, &format!("{} (let1 a 2 (+ a (car (cons 4 '(1 2)))))", *LET), "6");
|
||||
partial_eval_test(&g, &format!("{} (let1 a 2 (vau 0 p (+ 1 a)))", *LET), "None({})#[None/None/EnvID(0)/0/[]/Some(\"p\")/3]");
|
||||
partial_eval_test(&g, &format!("{}
|
||||
!(let1 a 2)
|
||||
(vau 0 p (+ 1 a))
|
||||
", *LET), "None({})#[None/None/EnvID(0)/0/[]/Some(\"p\")/3]");
|
||||
partial_eval_test(&g, &format!("{}
|
||||
!(let1 a 2)
|
||||
!(let1 b 5)
|
||||
(vau 0 p (+ b a))
|
||||
", *LET), "None({})#[None/None/EnvID(0)/0/[]/Some(\"p\")/7]");
|
||||
/*
|
||||
partial_eval_test(&g, &format!("{}
|
||||
(vau 0 p
|
||||
!(let1 a 2)
|
||||
!(let1 b 5)
|
||||
(+ b a)
|
||||
)
|
||||
", *LET), "None({})#[None/None/EnvID(0)/0/[]/Some(\"p\")/7]");
|
||||
partial_eval_test(&g, &format!("{}
|
||||
(vau d p
|
||||
!(let1 a 2)
|
||||
(+ (eval (car p) d) a)
|
||||
)
|
||||
", *LET), "None({})#[None/None/EnvID(2)/0/[]/Some(\"p\")/7]");
|
||||
*/
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fib_eval_test() { let g = grammar::TermParser::new(); let e = root_env();
|
||||
eval_test(true, &g, &e, &format!("{} (let1 x 10 (+ x 7))", *LET), 17);
|
||||
let def_fib = "
|
||||
!(let1 fib (vau de p
|
||||
!(let1 self (eval (car p) de))
|
||||
!(let1 n (eval (car (cdr p)) de))
|
||||
!(if (= 0 n) 0)
|
||||
!(if (= 1 n) 1)
|
||||
(+ (self self (- n 1)) (self self (- n 2)))
|
||||
))";
|
||||
eval_test(false, &g, &e, &format!("{} {} (fib fib 6)", *LET, def_fib), 8);
|
||||
}
|
||||
#[test]
|
||||
fn fact_eval_test() { let g = grammar::TermParser::new(); let e = root_env();
|
||||
let def_fact = "
|
||||
!(let1 fact (vau de p
|
||||
!(let1 self (eval (car p) de))
|
||||
!(let1 n (eval (car (cdr p)) de))
|
||||
!(if (= 0 n) 1)
|
||||
(* n (self self (- n 1)))
|
||||
))";
|
||||
eval_test(true, &g, &e, &format!("{} {} (fact fact 6)", *LET, def_fact), 720);
|
||||
}
|
||||
static VAPPLY: Lazy<String> = Lazy::new(|| {
|
||||
format!("
|
||||
{}
|
||||
!(let1 vapply (vau de p
|
||||
!(let1 f (eval (car p) de))
|
||||
!(let1 ip (eval (car (cdr p)) de))
|
||||
!(let1 nde (eval (car (cdr (cdr p))) de))
|
||||
(eval (cons f ip) nde)
|
||||
))", *LET)
|
||||
});
|
||||
#[test]
|
||||
fn vapply_eval_test() { let g = grammar::TermParser::new(); let e = root_env();
|
||||
// need the vapply to keep env in check because otherwise the env keeps growing
|
||||
// and the Rc::drop will overflow the stack lol
|
||||
let def_badid = format!("
|
||||
{}
|
||||
!(let1 badid (vau de p
|
||||
!(let1 inner (vau ide ip
|
||||
!(let1 self (car ip))
|
||||
!(let1 n (car (cdr ip)))
|
||||
!(let1 acc (car (cdr (cdr ip))))
|
||||
!(if (= 0 n) acc)
|
||||
(vapply self (cons self (cons (- n 1) (cons (+ acc 1) nil))) de)
|
||||
))
|
||||
(vapply inner (cons inner (cons (eval (car p) de) (cons 0 nil))) de)
|
||||
))", *VAPPLY);
|
||||
// Won't work unless tail calls work
|
||||
// so no PE?
|
||||
eval_test(false, &g, &e, &format!("{} (badid 1000)", def_badid), 1000);
|
||||
}
|
||||
|
||||
static VMAP: Lazy<String> = Lazy::new(|| {
|
||||
format!("
|
||||
{}
|
||||
!(let1 vmap (vau de p
|
||||
!(let1 vmap_inner (vau ide ip
|
||||
!(let1 self (car ip))
|
||||
!(let1 f (car (cdr ip)))
|
||||
!(let1 l (car (cdr (cdr ip))))
|
||||
!(if (= nil l) l)
|
||||
(cons (vapply f (cons (car l) nil) de) (vapply self (cons self (cons f (cons (cdr l) nil))) de))
|
||||
))
|
||||
(vapply vmap_inner (cons vmap_inner (cons (eval (car p) de) (cons (eval (car (cdr p)) de) nil))) de)
|
||||
))", *VAPPLY)
|
||||
});
|
||||
#[test]
|
||||
fn vmap_eval_test() { let g = grammar::TermParser::new(); let e = root_env();
|
||||
// Maybe define in terms of a right fold?
|
||||
//eval_test(true, &g, &e, &format!("{} (vmap (vau de p (+ 1 (car p))) '(1 2 3))", *VMAP), (2, (3, (4, Form::Nil))));
|
||||
eval_test(true, &g, &e, &format!("{} (vmap (vau de p (+ 1 (car p))) '(1))", *VMAP), (2, Form::Nil));
|
||||
}
|
||||
|
||||
static WRAP: Lazy<String> = Lazy::new(|| {
|
||||
format!("
|
||||
{}
|
||||
!(let1 wrap (vau de p
|
||||
!(let1 f (eval (car p) de))
|
||||
(vau ide p (vapply f (vmap (vau _ xp (eval (car xp) ide)) p) ide))
|
||||
))", *VMAP)
|
||||
});
|
||||
#[test]
|
||||
fn wrap_eval_test() { let g = grammar::TermParser::new(); let e = root_env();
|
||||
// Make sure (wrap (vau ...)) and internal style are optimized the same
|
||||
eval_test(true, &g, &e, &format!("{} ((wrap (vau _ p (+ (car p) 1))) (+ 1 2))", *WRAP), 4);
|
||||
}
|
||||
|
||||
static UNWRAP: Lazy<String> = Lazy::new(|| {
|
||||
format!("
|
||||
{}
|
||||
!(let1 unwrap (vau de p
|
||||
!(let1 f (eval (car p) de))
|
||||
(vau ide p (vapply f (vmap (vau _ xp (cons quote (cons (car xp) nil))) p) ide))
|
||||
))", *WRAP)
|
||||
});
|
||||
#[test]
|
||||
fn unwrap_eval_test() { let g = grammar::TermParser::new(); let e = root_env();
|
||||
// Can't represent prims in tests :( - they do work though, uncommenting and checking the
|
||||
// failed assert verifies
|
||||
//eval_test(true, &g, &e, &format!("{} ((unwrap (vau de p (car p))) (+ 1 2))", def_unwrap), ("quote", (("+", (1, (2, Form::Nil))), Form::Nil)));
|
||||
//eval_test(true, &g, &e, &format!("{} ((unwrap (vau de p (eval (car p) de))) (+ 1 2))", def_unwrap), (("+", (1, (2, Form::Nil))), Form::Nil));
|
||||
eval_test(true, &g, &e, &format!("{} ((unwrap (vau de p (eval (eval (car p) de) de))) (+ 1 2))", *UNWRAP), 3);
|
||||
eval_test(true, &g, &e, &format!("{} ((unwrap (vau de p (+ (eval (eval (car p) de) de) 1))) (+ 1 2))", *UNWRAP), 4);
|
||||
}
|
||||
|
||||
static LAPPLY: Lazy<String> = Lazy::new(|| {
|
||||
format!("
|
||||
{}
|
||||
!(let1 lapply (vau de p
|
||||
!(let1 f (eval (car p) de))
|
||||
!(let1 ip (eval (car (cdr p)) de))
|
||||
!(let1 nde (eval (car (cdr (cdr p))) de))
|
||||
(eval (cons (unwrap f) ip) nde)
|
||||
))", *UNWRAP)
|
||||
});
|
||||
#[test]
|
||||
fn lapply_eval_test() { let g = grammar::TermParser::new(); let e = root_env();
|
||||
// Should this allow envs at all? It technically can, but I feel like it kinda goes against the
|
||||
// sensible deriviation
|
||||
let def_lbadid = format!("
|
||||
{}
|
||||
!(let1 lbadid (vau de p
|
||||
!(let1 inner (wrap (vau ide ip
|
||||
!(let1 self (car ip))
|
||||
!(let1 n (car (cdr ip)))
|
||||
!(let1 acc (car (cdr (cdr ip))))
|
||||
!(if (= 0 n) acc)
|
||||
(lapply self (cons self (cons (- n 1) (cons (+ acc 1) nil))) de)
|
||||
)))
|
||||
(lapply inner (cons inner (cons (eval (car p) de) (cons 0 nil))) de)
|
||||
))", *LAPPLY);
|
||||
// Won't work unless tail calls work
|
||||
// takes a while though
|
||||
eval_test(false, &g, &e, &format!("{} (lbadid 1000)", def_lbadid), 1000);
|
||||
}
|
||||
|
||||
static VFOLDL: Lazy<String> = Lazy::new(|| {
|
||||
format!("
|
||||
{}
|
||||
!(let1 vfoldl (vau de p
|
||||
!(let1 vfoldl_inner (vau ide ip
|
||||
!(let1 self (car ip))
|
||||
!(let1 f (car (cdr ip)))
|
||||
!(let1 a (car (cdr (cdr ip))))
|
||||
!(let1 l (car (cdr (cdr (cdr ip)))))
|
||||
!(if (= nil l) a)
|
||||
(vapply self (cons self (cons f (cons (vapply f (cons a (cons (car l) nil)) de) (cons (cdr l) nil)))) de)
|
||||
))
|
||||
(vapply vfoldl_inner (cons vfoldl_inner (cons (eval (car p) de) (cons (eval (car (cdr p)) de) (cons (eval (car (cdr (cdr p))) de) nil)))) de)
|
||||
))", *LAPPLY)
|
||||
});
|
||||
#[test]
|
||||
fn vfoldl_eval_test() { let g = grammar::TermParser::new(); let e = root_env();
|
||||
eval_test(true, &g, &e, &format!("{} (vfoldl (vau de p (+ (car p) (car (cdr p)))) 0 '(1 2 3))", *VFOLDL), 6);
|
||||
}
|
||||
static ZIPD: Lazy<String> = Lazy::new(|| {
|
||||
format!("
|
||||
{}
|
||||
!(let1 zipd (vau de p
|
||||
!(let1 zipd_inner (vau ide ip
|
||||
!(let1 self (car ip))
|
||||
!(let1 a (car (cdr ip)))
|
||||
!(let1 b (car (cdr (cdr ip))))
|
||||
!(if (= nil a) a)
|
||||
!(if (= nil b) b)
|
||||
(cons (cons (car a) (car b)) (vapply self (cons self (cons (cdr a) (cons (cdr b) nil))) de))
|
||||
))
|
||||
(vapply zipd_inner (cons zipd_inner (cons (eval (car p) de) (cons (eval (car (cdr p)) de) nil))) de)
|
||||
))", *VFOLDL)
|
||||
});
|
||||
#[test]
|
||||
fn zipd_eval_test() { let g = grammar::TermParser::new(); let e = root_env();
|
||||
eval_test(true, &g, &e, &format!("{} (zipd '(1 2 3) '(4 5 6))", *ZIPD), ((1,4), ((2,5), ((3,6), Form::Nil))));
|
||||
}
|
||||
static CONCAT: Lazy<String> = Lazy::new(|| {
|
||||
format!("
|
||||
{}
|
||||
!(let1 concat (vau de p
|
||||
!(let1 concat_inner (vau ide ip
|
||||
!(let1 self (car ip))
|
||||
!(let1 a (car (cdr ip)))
|
||||
!(let1 b (car (cdr (cdr ip))))
|
||||
!(if (= nil a) b)
|
||||
(cons (car a) (vapply self (cons self (cons (cdr a) (cons b nil))) de))
|
||||
))
|
||||
(vapply concat_inner (cons concat_inner (cons (eval (car p) de) (cons (eval (car (cdr p)) de) nil))) de)
|
||||
))", *ZIPD)
|
||||
});
|
||||
|
||||
#[test]
|
||||
fn concat_eval_test() { let g = grammar::TermParser::new(); let e = root_env();
|
||||
eval_test(true, &g, &e, &format!("{} (concat '(1 2 3) '(4 5 6))", *CONCAT), (1, (2, (3, (4, (5, (6, Form::Nil)))))));
|
||||
}
|
||||
|
||||
static BVAU: Lazy<String> = Lazy::new(|| {
|
||||
format!("
|
||||
{}
|
||||
!(let1 match_params (wrap (vau 0 p
|
||||
!(let1 self (car p))
|
||||
!(let1 p_ls (car (cdr p)))
|
||||
!(let1 dp (car (cdr (cdr p))))
|
||||
!(let1 e (car (cdr (cdr (cdr p)))))
|
||||
!(if (= nil p_ls) (assert (= nil dp) e))
|
||||
!(if (symbol? p_ls) (cons (cons p_ls dp) e))
|
||||
(self self (cdr p_ls) (cdr dp) (self self (car p_ls) (car dp) e))
|
||||
)))
|
||||
!(let1 bvau (vau se p
|
||||
(if (= nil (cdr (cdr p)))
|
||||
; No de case
|
||||
!(let1 p_ls (car p))
|
||||
!(let1 b_v (car (cdr p)))
|
||||
(vau 0 dp
|
||||
(eval b_v (match_params match_params p_ls dp se))
|
||||
)
|
||||
|
||||
; de case
|
||||
!(let1 de_s (car p))
|
||||
!(let1 p_ls (car (cdr p)))
|
||||
!(let1 b_v (car (cdr (cdr p))))
|
||||
(vau dde dp
|
||||
(eval b_v (match_params match_params p_ls dp (cons (cons de_s dde) se)))
|
||||
)
|
||||
)
|
||||
))", *CONCAT)
|
||||
});
|
||||
#[test]
|
||||
fn bvau_eval_test() { let g = grammar::TermParser::new(); let e = root_env();
|
||||
eval_test(true, &g, &e, &format!("{} ((bvau _ (a b c) (+ a (- b c))) 10 2 3)", *BVAU), 9);
|
||||
//eval_test(true, &g, &e, &format!("{} ((bvau (a b c) (+ a (- b c))) 10 2 3)", *BVAU), 9);
|
||||
|
||||
//eval_test(true, &g, &e, &format!("{} ((bvau (a b . c) c) 10 2 3)", *BVAU), (3, Form::Nil));
|
||||
//eval_test(true, &g, &e, &format!("{} ((bvau (a b . c) c) 10 2)", *BVAU), Form::Nil);
|
||||
//eval_test(true, &g, &e, &format!("{} ((bvau (a b . c) c) 10 2 3 4 5)", *BVAU), (3, (4, (5, Form::Nil))));
|
||||
//eval_test(true, &g, &e, &format!("{} ((bvau c c) 3 4 5)", *BVAU), (3, (4, (5, Form::Nil))));
|
||||
//eval_test(true, &g, &e, &format!("{} ((bvau c c))", *BVAU), Form::Nil);
|
||||
//eval_test(true, &g, &e, &format!("{} ((bvau ((a b) . c) c) (10 2) 3 4 5)", *BVAU), (3, (4, (5, Form::Nil))));
|
||||
//eval_test(true, &g, &e, &format!("{} ((bvau ((a b) . c) a) (10 2) 3 4 5)", *BVAU), 10);
|
||||
//eval_test(true, &g, &e, &format!("{} ((bvau ((a b) . c) b) (10 2) 3 4 5)", *BVAU), 2);
|
||||
|
||||
//eval_test(true, &g, &e, &format!("{} ((wrap (bvau _ (a b c) (+ a (- b c)))) (+ 10 1) (+ 2 2) (+ 5 3))", *BVAU), 7);
|
||||
//eval_test(true, &g, &e, &format!("{} ((wrap (bvau (a b c) (+ a (- b c)))) (+ 10 1) (+ 2 2) (+ 5 3))", *BVAU), 7);
|
||||
}
|
||||
|
||||
static LAMBDA: Lazy<String> = Lazy::new(|| {
|
||||
format!("
|
||||
{}
|
||||
!(let1 lambda (vau de p
|
||||
(wrap (vapply bvau p de))
|
||||
))", *BVAU)
|
||||
});
|
||||
#[test]
|
||||
fn lambda_eval_test() { let g = grammar::TermParser::new(); let e = root_env();
|
||||
eval_test(true, &g, &e, &format!("{} ((lambda (a b c) (+ a (- b c))) (+ 10 1) (+ 2 2) (+ 5 3))", *LAMBDA), 7);
|
||||
|
||||
eval_test(true, &g, &e, &format!("{} ((lambda (a b . c) c) 10 2 3)", *LAMBDA), (3, Form::Nil));
|
||||
eval_test(true, &g, &e, &format!("{} ((lambda (a b . c) c) 10 2)", *LAMBDA), Form::Nil);
|
||||
eval_test(true, &g, &e, &format!("{} ((lambda (a b . c) c) 10 2 3 4 5)", *LAMBDA), (3, (4, (5, Form::Nil))));
|
||||
eval_test(true, &g, &e, &format!("{} ((lambda c c) 3 4 5)", *LAMBDA), (3, (4, (5, Form::Nil))));
|
||||
eval_test(true, &g, &e, &format!("{} ((lambda c c))", *LAMBDA), Form::Nil);
|
||||
eval_test(true, &g, &e, &format!("{} ((lambda ((a b) . c) c) '(10 2) 3 4 5)", *LAMBDA), (3, (4, (5, Form::Nil))));
|
||||
eval_test(true, &g, &e, &format!("{} ((lambda ((a b) . c) a) '(10 2) 3 4 5)", *LAMBDA), 10);
|
||||
eval_test(true, &g, &e, &format!("{} ((lambda ((a b) . c) b) '(10 2) 3 4 5)", *LAMBDA), 2);
|
||||
eval_test(true, &g, &e, &format!("{} ((lambda ((a b . c) d) b) '(10 2 3 4) 3)", *LAMBDA), 2);
|
||||
eval_test(true, &g, &e, &format!("{} ((lambda ((a b . c) d) c) '(10 2 3 4) 3)", *LAMBDA), (3, (4, Form::Nil)));
|
||||
// should fail
|
||||
//eval_test(true, &g, &e, &format!("{} ((lambda (a b c) c) 10 2 3 4)", *LAMBDA), 3);
|
||||
}
|
||||
|
||||
static LET2: Lazy<String> = Lazy::new(|| {
|
||||
format!("
|
||||
{}
|
||||
!(let1 let1 (bvau dp (s v b)
|
||||
(eval b (match_params match_params s (eval v dp) dp))
|
||||
))
|
||||
", *LAMBDA)
|
||||
});
|
||||
|
||||
#[test]
|
||||
fn let2_eval_test() { let g = grammar::TermParser::new(); let e = root_env();
|
||||
|
||||
eval_test(true, &g, &e, &format!("{} (let1 x (+ 10 1) (+ x 1))", *LET2), 12);
|
||||
eval_test(true, &g, &e, &format!("{} (let1 x '(10 1) x)", *LET2), (10, (1, Form::Nil)));
|
||||
eval_test(true, &g, &e, &format!("{} (let1 (a b) '(10 1) a)", *LET2), 10);
|
||||
eval_test(true, &g, &e, &format!("{} (let1 (a b) '(10 1) b)", *LET2), 1);
|
||||
eval_test(true, &g, &e, &format!("{} (let1 (a b . c) '(10 1) c)", *LET2), Form::Nil);
|
||||
eval_test(true, &g, &e, &format!("{} (let1 (a b . c) '(10 1 2 3) c)", *LET2), (2, (3, Form::Nil)));
|
||||
eval_test(true, &g, &e, &format!("{} (let1 ((a . b) . c) '((10 1) 2 3) a)", *LET2), 10);
|
||||
eval_test(true, &g, &e, &format!("{} (let1 ((a . b) . c) '((10 1) 2 3) b)", *LET2), (1, Form::Nil));
|
||||
// should fail
|
||||
//eval_test(true, &g, &e, &format!("{} (let1 (a b c) '(10 2 3 4) a)", *LET2), 10);
|
||||
}
|
||||
|
||||
static LIST: Lazy<String> = Lazy::new(|| {
|
||||
format!("
|
||||
{}
|
||||
!(let1 list (lambda args args))
|
||||
", *LET2)
|
||||
});
|
||||
|
||||
#[test]
|
||||
fn list_eval_test() { let g = grammar::TermParser::new(); let e = root_env();
|
||||
eval_test(true, &g, &e, &format!("{} (list 1 2 (+ 3 4))", *LIST), (1, (2, (7, Form::Nil))));
|
||||
}
|
||||
|
||||
static Y: Lazy<String> = Lazy::new(|| {
|
||||
format!("
|
||||
{}
|
||||
!(let1 Y (lambda (f3)
|
||||
((lambda (x1) (x1 x1))
|
||||
(lambda (x2) (f3 (wrap (vau app_env y (lapply (x2 x2) y app_env)))))))
|
||||
)
|
||||
", *LIST)
|
||||
});
|
||||
|
||||
#[test]
|
||||
fn y_eval_test() { let g = grammar::TermParser::new(); let e = root_env();
|
||||
|
||||
eval_test(true, &g, &e, &format!("{} ((Y (lambda (recurse) (lambda (n) (if (= 0 n) 1 (* n (recurse (- n 1))))))) 5)", *Y), 120);
|
||||
|
||||
}
|
||||
|
||||
static RLAMBDA: Lazy<String> = Lazy::new(|| {
|
||||
format!("
|
||||
{}
|
||||
!(let1 rlambda (bvau se (n p b)
|
||||
(eval (list Y (list lambda (list n) (list lambda p b))) se)
|
||||
))
|
||||
", *Y)
|
||||
});
|
||||
|
||||
#[test]
|
||||
fn rlambda_eval_test() { let g = grammar::TermParser::new(); let e = root_env();
|
||||
eval_test(true, &g, &e, &format!("{} ((rlambda recurse (n) (if (= 0 n) 1 (* n (recurse (- n 1))))) 5)", *RLAMBDA), 120);
|
||||
}
|
||||
static AND_OR: Lazy<String> = Lazy::new(|| {
|
||||
// need to extend for varidac
|
||||
format!("
|
||||
{}
|
||||
!(let1 and (bvau se (a b)
|
||||
!(let1 ae (eval a se))
|
||||
(if ae (eval b se) ae)
|
||||
))
|
||||
!(let1 or (bvau se (a b)
|
||||
!(let1 ae (eval a se))
|
||||
(if ae ae (eval b se))
|
||||
))
|
||||
", *RLAMBDA)
|
||||
});
|
||||
|
||||
#[test]
|
||||
fn and_or_eval_test() { let g = grammar::TermParser::new(); let e = root_env();
|
||||
eval_test(true, &g, &e, &format!("{} (and true true)", *AND_OR), true);
|
||||
eval_test(true, &g, &e, &format!("{} (and false true)", *AND_OR), false);
|
||||
eval_test(true, &g, &e, &format!("{} (and true false)", *AND_OR), false);
|
||||
eval_test(true, &g, &e, &format!("{} (and false false)", *AND_OR), false);
|
||||
|
||||
eval_test(true, &g, &e, &format!("{} (or true true)", *AND_OR), true);
|
||||
eval_test(true, &g, &e, &format!("{} (or false true)", *AND_OR), true);
|
||||
eval_test(true, &g, &e, &format!("{} (or true false)", *AND_OR), true);
|
||||
eval_test(true, &g, &e, &format!("{} (or false false)", *AND_OR), false);
|
||||
}
|
||||
static LEN: Lazy<String> = Lazy::new(|| {
|
||||
format!("
|
||||
{}
|
||||
!(let1 len (lambda (l)
|
||||
!(let1 len_helper (rlambda len_helper (l a)
|
||||
(if (pair? l) (len_helper (cdr l) (+ 1 a))
|
||||
a)
|
||||
))
|
||||
(len_helper l 0)
|
||||
))
|
||||
", *AND_OR)
|
||||
});
|
||||
|
||||
#[test]
|
||||
fn len_eval_test() { let g = grammar::TermParser::new(); let e = root_env();
|
||||
eval_test(true, &g, &e, &format!("{} (len '())", *LEN), 0);
|
||||
eval_test(true, &g, &e, &format!("{} (len '(1))", *LEN), 1);
|
||||
eval_test(true, &g, &e, &format!("{} (len '(1 2))", *LEN), 2);
|
||||
eval_test(true, &g, &e, &format!("{} (len '(1 2 3))", *LEN), 3);
|
||||
}
|
||||
static MATCH: Lazy<String> = Lazy::new(|| {
|
||||
format!("
|
||||
{}
|
||||
!(let1 match (bvau de (x . cases)
|
||||
!(let1 evaluate_case (rlambda evaluate_case (access c)
|
||||
!(if (symbol? c) (list true (lambda (b) (list let1 c access b))))
|
||||
!(if (and (pair? c) (= 'unquote (car c))) (list (list = access (car (cdr c))) (lambda (b) b)))
|
||||
!(if (and (pair? c) (= 'quote (car c))) (list (list = access c) (lambda (b) b)))
|
||||
!(if (pair? c)
|
||||
!(let1 tests (list and (list pair? access) (list = (len c) (list len access))))
|
||||
!(let1 (tests body_func) ((rlambda recurse (c tests access body_func) (if (pair? c)
|
||||
!(let1 (inner_test inner_body_func) (evaluate_case (list car access) (car c)))
|
||||
(recurse (cdr c)
|
||||
(list and tests inner_test)
|
||||
(list cdr access)
|
||||
(lambda (b) (body_func (inner_body_func b))))
|
||||
; else
|
||||
(list tests body_func)
|
||||
))
|
||||
c tests access (lambda (b) b)))
|
||||
(list tests body_func))
|
||||
(list (list = access c) (lambda (b) b))
|
||||
))
|
||||
!(let1 helper (rlambda helper (x_sym cases) (if (= nil cases) (list assert false)
|
||||
(let1 (test body_func) (evaluate_case x_sym (car cases))
|
||||
(concat (list if test (body_func (car (cdr cases)))) (list (helper x_sym (cdr (cdr cases)))))))))
|
||||
|
||||
(eval (list let1 '___MATCH_SYM x (helper '___MATCH_SYM cases)) de)
|
||||
;!(let1 expanded (list let1 '___MATCH_SYM x (helper '___MATCH_SYM cases)))
|
||||
;(debug expanded (eval expanded de))
|
||||
))
|
||||
", *LEN)
|
||||
});
|
||||
#[test]
|
||||
fn match_eval_test() { let g = grammar::TermParser::new(); let e = root_env();
|
||||
eval_test(true, &g, &e, &format!("{} (match (+ 1 2) 1 2 2 3 3 4 _ 0)", *MATCH), 4);
|
||||
eval_test(true, &g, &e, &format!("{} (match '(1 2) 1 2 2 3 3 4 _ 0)", *MATCH), 0);
|
||||
eval_test(true, &g, &e, &format!("{} (match '(1 2) 1 2 2 3 (a b) (+ a (+ 2 b)) _ 0)", *MATCH), 5);
|
||||
eval_test(true, &g, &e, &format!("{} (match '(1 2) 1 2 2 3 '(1 2) 7 _ 0)", *MATCH), 7);
|
||||
eval_test(true, &g, &e, &format!("{} (let1 a 70 (match (+ 60 10) (unquote a) 100 2 3 _ 0))", *MATCH), 100);
|
||||
}
|
||||
static RBTREE: Lazy<String> = Lazy::new(|| {
|
||||
format!("
|
||||
{}
|
||||
!(let1 empty (list 'B nil nil nil))
|
||||
!(let1 E empty)
|
||||
!(let1 EE (list 'BB nil nil nil))
|
||||
|
||||
!(let1 generic-foldl (rlambda generic-foldl (f z t) (match t
|
||||
(unquote E) z
|
||||
|
||||
(c a x b) !(let1 new_left_result (generic-foldl f z a))
|
||||
!(let1 folded (f new_left_result x))
|
||||
(generic-foldl f folded b))))
|
||||
|
||||
!(let1 blacken (lambda (t) (match t
|
||||
('R a x b) (list 'B a x b)
|
||||
t t)))
|
||||
!(let1 balance (lambda (t) (match t
|
||||
; figures 1 and 2
|
||||
('B ('R ('R a x b) y c) z d) (list 'R (list 'B a x b) y (list 'B c z d))
|
||||
('B ('R a x ('R b y c)) z d) (list 'R (list 'B a x b) y (list 'B c z d))
|
||||
('B a x ('R ('R b y c) z d)) (list 'R (list 'B a x b) y (list 'B c z d))
|
||||
('B a x ('R b y ('R c z d))) (list 'R (list 'B a x b) y (list 'B c z d))
|
||||
; figure 8, double black cases
|
||||
('BB ('R a x ('R b y c)) z d) (list 'B (list 'B a x b) y (list 'B c z d))
|
||||
('BB a x ('R ('R b y c) z d)) (list 'B (list 'B a x b) y (list 'B c z d))
|
||||
; already balenced
|
||||
t t)))
|
||||
|
||||
!(let1 map-insert !(let1 ins (rlambda ins (t k v) (match t
|
||||
(unquote E) (list 'R t (list k v) t)
|
||||
(c a x b) !(if (< k (car x)) (balance (list c (ins a k v) x b)))
|
||||
!(if (= k (car x)) (list c a (list k v) b))
|
||||
(balance (list c a x (ins b k v))))))
|
||||
(lambda (t k v) (blacken (ins t k v))))
|
||||
|
||||
!(let1 map-empty empty)
|
||||
|
||||
!(let1 make-test-tree (rlambda make-test-tree (n t) (if (<= n 0) t
|
||||
(make-test-tree (- n 1) (map-insert t n (= 0 (% n 10)))))))
|
||||
!(let1 reduce-test-tree (lambda (tree) (generic-foldl (lambda (a x) (if (car (cdr x)) (+ a 1) a)) 0 tree)))
|
||||
", *MATCH)
|
||||
});
|
||||
#[test]
|
||||
fn rbtree_eval_test() { let g = grammar::TermParser::new(); let e = root_env();
|
||||
eval_test(false, &g, &e, &format!("{} (reduce-test-tree (make-test-tree 10 map-empty))", *RBTREE), 1);
|
||||
//eval_test(false, &g, &e, &format!("{} (reduce-test-tree (make-test-tree 20 map-empty))", *RBTREE), 2);
|
||||
}
|
||||
|
||||
1042
kr/src/pe_ast.rs
Normal file
1042
kr/src/pe_ast.rs
Normal file
File diff suppressed because it is too large
Load Diff
617
kr/src/test.rs
Normal file
617
kr/src/test.rs
Normal file
@@ -0,0 +1,617 @@
|
||||
use std::rc::Rc;
|
||||
|
||||
use crate::grammar;
|
||||
use crate::ast::{eval,root_env,Form,PossibleTailCall};
|
||||
use crate::pe_ast::{mark,partial_eval,new_base_ctxs,MarkedForm};
|
||||
|
||||
#[test]
|
||||
fn parse_test() {
|
||||
let g = grammar::TermParser::new();
|
||||
for test in [
|
||||
"22", "(22)", "(((22)))",
|
||||
"(22 )", "()", "( )", "( 44)", "(44 )",
|
||||
"(22 44 (1) 33 (4 5 (6) 6))", "hello",
|
||||
"-", "+", "(+ 1 ;hi
|
||||
3)", "'13", "hello-world", "_",
|
||||
] {
|
||||
assert!(g.parse(test).is_ok());
|
||||
}
|
||||
assert!(g.parse("((22)").is_err());
|
||||
}
|
||||
|
||||
fn eval_test<T: Into<Form>>(also_pe: bool, gram: &grammar::TermParser, e: &Rc<Form>, code: &str, expected: T) {
|
||||
println!("Doing test {}", code);
|
||||
let parsed = Rc::new(gram.parse(code).unwrap());
|
||||
let basic_result = eval(Rc::clone(e), Rc::clone(&parsed));
|
||||
assert_eq!(*basic_result, expected.into());
|
||||
if also_pe {
|
||||
let (bctx, dctx) = new_base_ctxs();
|
||||
let (bctx, marked) = mark(parsed,bctx);
|
||||
let unvaled = marked.unval().unwrap();
|
||||
let (bctx, ped) = partial_eval(bctx, dctx, unvaled).unwrap();
|
||||
let (bctx, marked_basic_result) = mark(basic_result,bctx);
|
||||
println!("Final PE {}", ped);
|
||||
println!("wanted {}", marked_basic_result);
|
||||
assert_eq!(*ped, *marked_basic_result);
|
||||
}
|
||||
}
|
||||
fn partial_eval_test(gram: &grammar::TermParser, code: &str, expected: &str) {
|
||||
println!("Doing PE test {}", code);
|
||||
let parsed = Rc::new(gram.parse(code).unwrap());
|
||||
let (bctx, dctx) = new_base_ctxs();
|
||||
let (bctx, marked) = mark(parsed,bctx);
|
||||
let unvaled = marked.unval().unwrap();
|
||||
let (bctx, ped) = partial_eval(bctx, dctx, unvaled).unwrap();
|
||||
println!("Final PE {}", ped);
|
||||
println!("wanted {}", expected);
|
||||
assert_eq!(format!("{}", ped), expected);
|
||||
}
|
||||
#[test]
|
||||
fn basic_pe_test() {
|
||||
let g = grammar::TermParser::new();
|
||||
partial_eval_test(&g, "(+ 2 (car (cons 4 '(1 2))))", "6");
|
||||
partial_eval_test(&g, "(vau 0 p (+ 1 2))", "NeededIds { heads: {}, tails: {}, body_stopped: {}, if_stopped: {} }#[None/None/EnvID(1)/0/[]/Some(\"p\")/3]");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn basic_eval_test() { let g = grammar::TermParser::new(); let e = root_env();
|
||||
eval_test(true, &g, &e, "(+ 2 (car (cons 4 '(1 2))))", 6);
|
||||
eval_test(true, &g, &e, "(= 17 ((vau d p (+ (eval (car p) d) 13)) (+ 1 3)))", true);
|
||||
eval_test(true, &g, &e, "(if (= 2 2) (+ 1 2) (+ 3 4))", 3);
|
||||
eval_test(true, &g, &e, "(quote a)", "a");
|
||||
eval_test(true, &g, &e, "'a", "a");
|
||||
eval_test(true, &g, &e, "'(1 . a)", (1, "a"));
|
||||
eval_test(true, &g, &e, "'(1 a)", (1, ("a", Form::Nil)));
|
||||
eval_test(true, &g, &e, "true", true);
|
||||
eval_test(true, &g, &e, "false", false);
|
||||
eval_test(true, &g, &e, "nil", Form::Nil);
|
||||
|
||||
eval_test(true, &g, &e, "(+ 1 2)", 3);
|
||||
eval_test(true, &g, &e, "(- 1 2)", -1);
|
||||
eval_test(true, &g, &e, "(* 1 2)", 2);
|
||||
eval_test(true, &g, &e, "(/ 4 2)", 2);
|
||||
eval_test(true, &g, &e, "(% 3 2)", 1);
|
||||
eval_test(true, &g, &e, "(& 3 2)", 2);
|
||||
eval_test(true, &g, &e, "(| 2 1)", 3);
|
||||
eval_test(true, &g, &e, "(^ 2 1)", 3);
|
||||
eval_test(true, &g, &e, "(^ 3 1)", 2);
|
||||
|
||||
eval_test(true, &g, &e, "(< 3 1)", false);
|
||||
eval_test(true, &g, &e, "(<= 3 1)", false);
|
||||
eval_test(true, &g, &e, "(> 3 1)", true);
|
||||
eval_test(true, &g, &e, "(>= 3 1)", true);
|
||||
|
||||
eval_test(true, &g, &e, "(comb? +)", true);
|
||||
eval_test(true, &g, &e, "(comb? (vau d p 1))", true);
|
||||
eval_test(true, &g, &e, "(comb? 1)", false);
|
||||
eval_test(true, &g, &e, "(pair? '(a))", true);
|
||||
//eval_test(true, &g, &e, "(pair? '())", true);
|
||||
eval_test(true, &g, &e, "(nil? nil)", true);
|
||||
eval_test(true, &g, &e, "(nil? 1)", false);
|
||||
eval_test(true, &g, &e, "(pair? 1)", false);
|
||||
eval_test(true, &g, &e, "(symbol? 'a)", true);
|
||||
eval_test(true, &g, &e, "(symbol? 1)", false);
|
||||
eval_test(true, &g, &e, "(int? 1)", true);
|
||||
eval_test(true, &g, &e, "(int? true)", false);
|
||||
eval_test(true, &g, &e, "(bool? true)", true);
|
||||
eval_test(true, &g, &e, "(bool? 1)", false);
|
||||
|
||||
eval_test(true, &g, &e, "!(bool?) 1", false);
|
||||
eval_test(true, &g, &e, "!(bool?) true", true);
|
||||
|
||||
eval_test(true, &g, &e, "((vau root_env _ (eval 'a (cons (cons 'a 2) root_env))))", 2);
|
||||
eval_test(true, &g, &e, "'name-dash", "name-dash");
|
||||
}
|
||||
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
static LET: Lazy<String> = Lazy::new(|| {
|
||||
"!((vau root_env p (eval (car p)
|
||||
(cons (cons 'let1
|
||||
(vau de p (eval (car (cdr (cdr p))) (cons (cons (car p) (eval (car (cdr p)) de)) de)))
|
||||
) root_env))))".to_owned()
|
||||
});
|
||||
|
||||
#[test]
|
||||
fn let_pe_test() {
|
||||
let g = grammar::TermParser::new();
|
||||
partial_eval_test(&g, &format!("{} (let1 a 2 (+ a (car (cons 4 '(1 2)))))", *LET), "6");
|
||||
partial_eval_test(&g, &format!("{} (let1 a 2 (vau 0 p (+ 1 a)))", *LET),"NeededIds { heads: {}, tails: {}, body_stopped: {}, if_stopped: {} }#[None/None/EnvID(3)/0/[]/Some(\"p\")/3]");
|
||||
partial_eval_test(&g, &format!("{}
|
||||
!(let1 a 2)
|
||||
(vau 0 p (+ 1 a))
|
||||
", *LET), "NeededIds { heads: {}, tails: {}, body_stopped: {}, if_stopped: {} }#[None/None/EnvID(3)/0/[]/Some(\"p\")/3]");
|
||||
partial_eval_test(&g, &format!("{}
|
||||
!(let1 a 2)
|
||||
!(let1 b 5)
|
||||
(vau 0 p (+ b a))
|
||||
", *LET), "NeededIds { heads: {}, tails: {}, body_stopped: {}, if_stopped: {} }#[None/None/EnvID(3)/0/[]/Some(\"p\")/7]");
|
||||
/*
|
||||
partial_eval_test(&g, &format!("{}
|
||||
(vau 0 p
|
||||
!(let1 a 2)
|
||||
!(let1 b 5)
|
||||
(+ b a)
|
||||
)
|
||||
", *LET), "None({})#[None/None/EnvID(0)/0/[]/Some(\"p\")/7]");
|
||||
partial_eval_test(&g, &format!("{}
|
||||
(vau d p
|
||||
!(let1 a 2)
|
||||
(+ (eval (car p) d) a)
|
||||
)
|
||||
", *LET), "None({})#[None/None/EnvID(2)/0/[]/Some(\"p\")/7]");
|
||||
*/
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fib_eval_test() { let g = grammar::TermParser::new(); let e = root_env();
|
||||
eval_test(true, &g, &e, &format!("{} (let1 x 10 (+ x 7))", *LET), 17);
|
||||
let def_fib = "
|
||||
!(let1 fib (vau de p
|
||||
!(let1 self (eval (car p) de))
|
||||
!(let1 n (eval (car (cdr p)) de))
|
||||
!(if (= 0 n) 0)
|
||||
!(if (= 1 n) 1)
|
||||
(+ (self self (- n 1)) (self self (- n 2)))
|
||||
))";
|
||||
eval_test(false, &g, &e, &format!("{} {} (fib fib 6)", *LET, def_fib), 8);
|
||||
}
|
||||
#[test]
|
||||
fn fact_eval_test() { let g = grammar::TermParser::new(); let e = root_env();
|
||||
let def_fact = "
|
||||
!(let1 fact (vau de p
|
||||
!(let1 self (eval (car p) de))
|
||||
!(let1 n (eval (car (cdr p)) de))
|
||||
!(if (= 0 n) 1)
|
||||
(* n (self self (- n 1)))
|
||||
))";
|
||||
eval_test(true, &g, &e, &format!("{} {} (fact fact 6)", *LET, def_fact), 720);
|
||||
}
|
||||
static VAPPLY: Lazy<String> = Lazy::new(|| {
|
||||
format!("
|
||||
{}
|
||||
!(let1 vapply (vau de p
|
||||
!(let1 f (eval (car p) de))
|
||||
!(let1 ip (eval (car (cdr p)) de))
|
||||
!(let1 nde (eval (car (cdr (cdr p))) de))
|
||||
(eval (cons f ip) nde)
|
||||
))", *LET)
|
||||
});
|
||||
#[test]
|
||||
fn vapply_eval_test() { let g = grammar::TermParser::new(); let e = root_env();
|
||||
// need the vapply to keep env in check because otherwise the env keeps growing
|
||||
// and the Rc::drop will overflow the stack lol
|
||||
let def_badid = format!("
|
||||
{}
|
||||
!(let1 badid (vau de p
|
||||
!(let1 inner (vau ide ip
|
||||
!(let1 self (car ip))
|
||||
!(let1 n (car (cdr ip)))
|
||||
!(let1 acc (car (cdr (cdr ip))))
|
||||
!(if (= 0 n) acc)
|
||||
(vapply self (cons self (cons (- n 1) (cons (+ acc 1) nil))) de)
|
||||
))
|
||||
(vapply inner (cons inner (cons (eval (car p) de) (cons 0 nil))) de)
|
||||
))", *VAPPLY);
|
||||
// Won't work unless tail calls work
|
||||
// so no PE?
|
||||
eval_test(false, &g, &e, &format!("{} (badid 1000)", def_badid), 1000);
|
||||
}
|
||||
|
||||
static VMAP: Lazy<String> = Lazy::new(|| {
|
||||
format!("
|
||||
{}
|
||||
!(let1 vmap (vau de p
|
||||
!(let1 vmap_inner (vau ide ip
|
||||
!(let1 self (car ip))
|
||||
!(let1 f (car (cdr ip)))
|
||||
!(let1 l (car (cdr (cdr ip))))
|
||||
!(if (= nil l) l)
|
||||
(cons (vapply f (cons (car l) nil) de) (vapply self (cons self (cons f (cons (cdr l) nil))) de))
|
||||
))
|
||||
(vapply vmap_inner (cons vmap_inner (cons (eval (car p) de) (cons (eval (car (cdr p)) de) nil))) de)
|
||||
))", *VAPPLY)
|
||||
});
|
||||
#[test]
|
||||
fn vmap_eval_test() { let g = grammar::TermParser::new(); let e = root_env();
|
||||
// Maybe define in terms of a right fold?
|
||||
//eval_test(true, &g, &e, &format!("{} (vmap (vau de p (+ 1 (car p))) '(1 2 3))", *VMAP), (2, (3, (4, Form::Nil))));
|
||||
eval_test(true, &g, &e, &format!("{} (vmap (vau de p (+ 1 (car p))) '(1))", *VMAP), (2, Form::Nil));
|
||||
}
|
||||
|
||||
static WRAP: Lazy<String> = Lazy::new(|| {
|
||||
format!("
|
||||
{}
|
||||
!(let1 wrap (vau de p
|
||||
!(let1 f (eval (car p) de))
|
||||
(vau ide p (vapply f (vmap (vau _ xp (eval (car xp) ide)) p) ide))
|
||||
))", *VMAP)
|
||||
});
|
||||
#[test]
|
||||
fn wrap_eval_test() { let g = grammar::TermParser::new(); let e = root_env();
|
||||
// Make sure (wrap (vau ...)) and internal style are optimized the same
|
||||
eval_test(true, &g, &e, &format!("{} ((wrap (vau _ p (+ (car p) 1))) (+ 1 2))", *WRAP), 4);
|
||||
}
|
||||
|
||||
static UNWRAP: Lazy<String> = Lazy::new(|| {
|
||||
format!("
|
||||
{}
|
||||
!(let1 unwrap (vau de p
|
||||
!(let1 f (eval (car p) de))
|
||||
(vau ide p (vapply f (vmap (vau _ xp (cons quote (cons (car xp) nil))) p) ide))
|
||||
))", *WRAP)
|
||||
});
|
||||
#[test]
|
||||
fn unwrap_eval_test() { let g = grammar::TermParser::new(); let e = root_env();
|
||||
// Can't represent prims in tests :( - they do work though, uncommenting and checking the
|
||||
// failed assert verifies
|
||||
//eval_test(true, &g, &e, &format!("{} ((unwrap (vau de p (car p))) (+ 1 2))", def_unwrap), ("quote", (("+", (1, (2, Form::Nil))), Form::Nil)));
|
||||
//eval_test(true, &g, &e, &format!("{} ((unwrap (vau de p (eval (car p) de))) (+ 1 2))", def_unwrap), (("+", (1, (2, Form::Nil))), Form::Nil));
|
||||
eval_test(true, &g, &e, &format!("{} ((unwrap (vau de p (eval (eval (car p) de) de))) (+ 1 2))", *UNWRAP), 3);
|
||||
eval_test(true, &g, &e, &format!("{} ((unwrap (vau de p (+ (eval (eval (car p) de) de) 1))) (+ 1 2))", *UNWRAP), 4);
|
||||
}
|
||||
|
||||
static LAPPLY: Lazy<String> = Lazy::new(|| {
|
||||
format!("
|
||||
{}
|
||||
!(let1 lapply (vau de p
|
||||
!(let1 f (eval (car p) de))
|
||||
!(let1 ip (eval (car (cdr p)) de))
|
||||
!(let1 nde (eval (car (cdr (cdr p))) de))
|
||||
(eval (cons (unwrap f) ip) nde)
|
||||
))", *UNWRAP)
|
||||
});
|
||||
#[test]
|
||||
fn lapply_eval_test() { let g = grammar::TermParser::new(); let e = root_env();
|
||||
// Should this allow envs at all? It technically can, but I feel like it kinda goes against the
|
||||
// sensible deriviation
|
||||
let def_lbadid = format!("
|
||||
{}
|
||||
!(let1 lbadid (vau de p
|
||||
!(let1 inner (wrap (vau ide ip
|
||||
!(let1 self (car ip))
|
||||
!(let1 n (car (cdr ip)))
|
||||
!(let1 acc (car (cdr (cdr ip))))
|
||||
!(if (= 0 n) acc)
|
||||
(lapply self (cons self (cons (- n 1) (cons (+ acc 1) nil))) de)
|
||||
)))
|
||||
(lapply inner (cons inner (cons (eval (car p) de) (cons 0 nil))) de)
|
||||
))", *LAPPLY);
|
||||
// Won't work unless tail calls work
|
||||
// takes a while though
|
||||
eval_test(false, &g, &e, &format!("{} (lbadid 1000)", def_lbadid), 1000);
|
||||
}
|
||||
|
||||
static VFOLDL: Lazy<String> = Lazy::new(|| {
|
||||
format!("
|
||||
{}
|
||||
!(let1 vfoldl (vau de p
|
||||
!(let1 vfoldl_inner (vau ide ip
|
||||
!(let1 self (car ip))
|
||||
!(let1 f (car (cdr ip)))
|
||||
!(let1 a (car (cdr (cdr ip))))
|
||||
!(let1 l (car (cdr (cdr (cdr ip)))))
|
||||
!(if (= nil l) a)
|
||||
(vapply self (cons self (cons f (cons (vapply f (cons a (cons (car l) nil)) de) (cons (cdr l) nil)))) de)
|
||||
))
|
||||
(vapply vfoldl_inner (cons vfoldl_inner (cons (eval (car p) de) (cons (eval (car (cdr p)) de) (cons (eval (car (cdr (cdr p))) de) nil)))) de)
|
||||
))", *LAPPLY)
|
||||
});
|
||||
#[test]
|
||||
fn vfoldl_eval_test() { let g = grammar::TermParser::new(); let e = root_env();
|
||||
eval_test(true, &g, &e, &format!("{} (vfoldl (vau de p (+ (car p) (car (cdr p)))) 0 '(1 2 3))", *VFOLDL), 6);
|
||||
}
|
||||
static ZIPD: Lazy<String> = Lazy::new(|| {
|
||||
format!("
|
||||
{}
|
||||
!(let1 zipd (vau de p
|
||||
!(let1 zipd_inner (vau ide ip
|
||||
!(let1 self (car ip))
|
||||
!(let1 a (car (cdr ip)))
|
||||
!(let1 b (car (cdr (cdr ip))))
|
||||
!(if (= nil a) a)
|
||||
!(if (= nil b) b)
|
||||
(cons (cons (car a) (car b)) (vapply self (cons self (cons (cdr a) (cons (cdr b) nil))) de))
|
||||
))
|
||||
(vapply zipd_inner (cons zipd_inner (cons (eval (car p) de) (cons (eval (car (cdr p)) de) nil))) de)
|
||||
))", *VFOLDL)
|
||||
});
|
||||
#[test]
|
||||
fn zipd_eval_test() { let g = grammar::TermParser::new(); let e = root_env();
|
||||
eval_test(true, &g, &e, &format!("{} (zipd '(1 2 3) '(4 5 6))", *ZIPD), ((1,4), ((2,5), ((3,6), Form::Nil))));
|
||||
}
|
||||
static CONCAT: Lazy<String> = Lazy::new(|| {
|
||||
format!("
|
||||
{}
|
||||
!(let1 concat (vau de p
|
||||
!(let1 concat_inner (vau ide ip
|
||||
!(let1 self (car ip))
|
||||
!(let1 a (car (cdr ip)))
|
||||
!(let1 b (car (cdr (cdr ip))))
|
||||
!(if (= nil a) b)
|
||||
(cons (car a) (vapply self (cons self (cons (cdr a) (cons b nil))) de))
|
||||
))
|
||||
(vapply concat_inner (cons concat_inner (cons (eval (car p) de) (cons (eval (car (cdr p)) de) nil))) de)
|
||||
))", *ZIPD)
|
||||
});
|
||||
|
||||
#[test]
|
||||
fn concat_eval_test() { let g = grammar::TermParser::new(); let e = root_env();
|
||||
eval_test(true, &g, &e, &format!("{} (concat '(1 2 3) '(4 5 6))", *CONCAT), (1, (2, (3, (4, (5, (6, Form::Nil)))))));
|
||||
}
|
||||
|
||||
static BVAU: Lazy<String> = Lazy::new(|| {
|
||||
format!("
|
||||
{}
|
||||
!(let1 match_params (wrap (vau 0 p
|
||||
!(let1 self (car p))
|
||||
!(let1 p_ls (car (cdr p)))
|
||||
!(let1 dp (car (cdr (cdr p))))
|
||||
!(let1 e (car (cdr (cdr (cdr p)))))
|
||||
!(if (= nil p_ls) (assert (= nil dp) e))
|
||||
!(if (symbol? p_ls) (cons (cons p_ls dp) e))
|
||||
(self self (cdr p_ls) (cdr dp) (self self (car p_ls) (car dp) e))
|
||||
)))
|
||||
!(let1 bvau (vau se p
|
||||
(if (= nil (cdr (cdr p)))
|
||||
; No de case
|
||||
!(let1 p_ls (car p))
|
||||
!(let1 b_v (car (cdr p)))
|
||||
(vau 0 dp
|
||||
(eval b_v (match_params match_params p_ls dp se))
|
||||
)
|
||||
|
||||
; de case
|
||||
!(let1 de_s (car p))
|
||||
!(let1 p_ls (car (cdr p)))
|
||||
!(let1 b_v (car (cdr (cdr p))))
|
||||
(vau dde dp
|
||||
(eval b_v (match_params match_params p_ls dp (cons (cons de_s dde) se)))
|
||||
)
|
||||
)
|
||||
))", *CONCAT)
|
||||
});
|
||||
#[test]
|
||||
fn bvau_eval_test() { let g = grammar::TermParser::new(); let e = root_env();
|
||||
eval_test(true, &g, &e, &format!("{} ((bvau _ (a b c) (+ a (- b c))) 10 2 3)", *BVAU), 9);
|
||||
//eval_test(true, &g, &e, &format!("{} ((bvau (a b c) (+ a (- b c))) 10 2 3)", *BVAU), 9);
|
||||
|
||||
//eval_test(true, &g, &e, &format!("{} ((bvau (a b . c) c) 10 2 3)", *BVAU), (3, Form::Nil));
|
||||
//eval_test(true, &g, &e, &format!("{} ((bvau (a b . c) c) 10 2)", *BVAU), Form::Nil);
|
||||
//eval_test(true, &g, &e, &format!("{} ((bvau (a b . c) c) 10 2 3 4 5)", *BVAU), (3, (4, (5, Form::Nil))));
|
||||
//eval_test(true, &g, &e, &format!("{} ((bvau c c) 3 4 5)", *BVAU), (3, (4, (5, Form::Nil))));
|
||||
//eval_test(true, &g, &e, &format!("{} ((bvau c c))", *BVAU), Form::Nil);
|
||||
//eval_test(true, &g, &e, &format!("{} ((bvau ((a b) . c) c) (10 2) 3 4 5)", *BVAU), (3, (4, (5, Form::Nil))));
|
||||
//eval_test(true, &g, &e, &format!("{} ((bvau ((a b) . c) a) (10 2) 3 4 5)", *BVAU), 10);
|
||||
//eval_test(true, &g, &e, &format!("{} ((bvau ((a b) . c) b) (10 2) 3 4 5)", *BVAU), 2);
|
||||
|
||||
//eval_test(true, &g, &e, &format!("{} ((wrap (bvau _ (a b c) (+ a (- b c)))) (+ 10 1) (+ 2 2) (+ 5 3))", *BVAU), 7);
|
||||
//eval_test(true, &g, &e, &format!("{} ((wrap (bvau (a b c) (+ a (- b c)))) (+ 10 1) (+ 2 2) (+ 5 3))", *BVAU), 7);
|
||||
}
|
||||
|
||||
static LAMBDA: Lazy<String> = Lazy::new(|| {
|
||||
format!("
|
||||
{}
|
||||
!(let1 lambda (vau de p
|
||||
(wrap (vapply bvau p de))
|
||||
))", *BVAU)
|
||||
});
|
||||
#[test]
|
||||
fn lambda_eval_test() { let g = grammar::TermParser::new(); let e = root_env();
|
||||
eval_test(true, &g, &e, &format!("{} ((lambda (a b c) (+ a (- b c))) (+ 10 1) (+ 2 2) (+ 5 3))", *LAMBDA), 7);
|
||||
eval_test(true, &g, &e, &format!("{} ((lambda (a b . c) c) 10 2 3)", *LAMBDA), (3, Form::Nil));
|
||||
eval_test(true, &g, &e, &format!("{} ((lambda (a b . c) c) 10 2)", *LAMBDA), Form::Nil);
|
||||
eval_test(true, &g, &e, &format!("{} ((lambda (a b . c) c) 10 2 3 4 5)", *LAMBDA), (3, (4, (5, Form::Nil))));
|
||||
eval_test(true, &g, &e, &format!("{} ((lambda c c) 3 4 5)", *LAMBDA), (3, (4, (5, Form::Nil))));
|
||||
eval_test(true, &g, &e, &format!("{} ((lambda c c))", *LAMBDA), Form::Nil);
|
||||
eval_test(true, &g, &e, &format!("{} ((lambda ((a b) . c) c) '(10 2) 3 4 5)", *LAMBDA), (3, (4, (5, Form::Nil))));
|
||||
eval_test(true, &g, &e, &format!("{} ((lambda ((a b) . c) a) '(10 2) 3 4 5)", *LAMBDA), 10);
|
||||
eval_test(true, &g, &e, &format!("{} ((lambda ((a b) . c) b) '(10 2) 3 4 5)", *LAMBDA), 2);
|
||||
eval_test(true, &g, &e, &format!("{} ((lambda ((a b . c) d) b) '(10 2 3 4) 3)", *LAMBDA), 2);
|
||||
eval_test(true, &g, &e, &format!("{} ((lambda ((a b . c) d) c) '(10 2 3 4) 3)", *LAMBDA), (3, (4, Form::Nil)));
|
||||
// should fail
|
||||
//eval_test(true, &g, &e, &format!("{} ((lambda (a b c) c) 10 2 3 4)", *LAMBDA), 3);
|
||||
}
|
||||
|
||||
static LET2: Lazy<String> = Lazy::new(|| {
|
||||
format!("
|
||||
{}
|
||||
!(let1 let1 (bvau dp (s v b)
|
||||
(eval b (match_params match_params s (eval v dp) dp))
|
||||
))
|
||||
", *LAMBDA)
|
||||
});
|
||||
|
||||
#[test]
|
||||
fn let2_eval_test() { let g = grammar::TermParser::new(); let e = root_env();
|
||||
|
||||
eval_test(true, &g, &e, &format!("{} (let1 x (+ 10 1) (+ x 1))", *LET2), 12);
|
||||
eval_test(true, &g, &e, &format!("{} (let1 x '(10 1) x)", *LET2), (10, (1, Form::Nil)));
|
||||
eval_test(true, &g, &e, &format!("{} (let1 (a b) '(10 1) a)", *LET2), 10);
|
||||
eval_test(true, &g, &e, &format!("{} (let1 (a b) '(10 1) b)", *LET2), 1);
|
||||
eval_test(true, &g, &e, &format!("{} (let1 (a b . c) '(10 1) c)", *LET2), Form::Nil);
|
||||
eval_test(true, &g, &e, &format!("{} (let1 (a b . c) '(10 1 2 3) c)", *LET2), (2, (3, Form::Nil)));
|
||||
eval_test(true, &g, &e, &format!("{} (let1 ((a . b) . c) '((10 1) 2 3) a)", *LET2), 10);
|
||||
eval_test(true, &g, &e, &format!("{} (let1 ((a . b) . c) '((10 1) 2 3) b)", *LET2), (1, Form::Nil));
|
||||
// should fail
|
||||
//eval_test(true, &g, &e, &format!("{} (let1 (a b c) '(10 2 3 4) a)", *LET2), 10);
|
||||
}
|
||||
|
||||
static LIST: Lazy<String> = Lazy::new(|| {
|
||||
format!("
|
||||
{}
|
||||
!(let1 list (lambda args args))
|
||||
", *LET2)
|
||||
});
|
||||
|
||||
#[test]
|
||||
fn list_eval_test() { let g = grammar::TermParser::new(); let e = root_env();
|
||||
eval_test(true, &g, &e, &format!("{} (list 1 2 (+ 3 4))", *LIST), (1, (2, (7, Form::Nil))));
|
||||
}
|
||||
|
||||
static Y: Lazy<String> = Lazy::new(|| {
|
||||
format!("
|
||||
{}
|
||||
!(let1 Y (lambda (f3)
|
||||
((lambda (x1) (x1 x1))
|
||||
(lambda (x2) (f3 (wrap (vau app_env y (lapply (x2 x2) y app_env)))))))
|
||||
)
|
||||
", *LIST)
|
||||
});
|
||||
|
||||
#[test]
|
||||
fn y_eval_test() { let g = grammar::TermParser::new(); let e = root_env();
|
||||
|
||||
eval_test(true, &g, &e, &format!("{} ((Y (lambda (recurse) (lambda (n) (if (= 0 n) 1 (* n (recurse (- n 1))))))) 5)", *Y), 120);
|
||||
|
||||
}
|
||||
|
||||
static RLAMBDA: Lazy<String> = Lazy::new(|| {
|
||||
format!("
|
||||
{}
|
||||
!(let1 rlambda (bvau se (n p b)
|
||||
(eval (list Y (list lambda (list n) (list lambda p b))) se)
|
||||
))
|
||||
", *Y)
|
||||
});
|
||||
|
||||
#[test]
|
||||
fn rlambda_eval_test() { let g = grammar::TermParser::new(); let e = root_env();
|
||||
eval_test(true, &g, &e, &format!("{} ((rlambda recurse (n) (if (= 0 n) 1 (* n (recurse (- n 1))))) 5)", *RLAMBDA), 120);
|
||||
}
|
||||
static AND_OR: Lazy<String> = Lazy::new(|| {
|
||||
// need to extend for varidac
|
||||
format!("
|
||||
{}
|
||||
!(let1 and (bvau se (a b)
|
||||
!(let1 ae (eval a se))
|
||||
(if ae (eval b se) ae)
|
||||
))
|
||||
!(let1 or (bvau se (a b)
|
||||
!(let1 ae (eval a se))
|
||||
(if ae ae (eval b se))
|
||||
))
|
||||
", *RLAMBDA)
|
||||
});
|
||||
|
||||
#[test]
|
||||
fn and_or_eval_test() { let g = grammar::TermParser::new(); let e = root_env();
|
||||
eval_test(true, &g, &e, &format!("{} (and true true)", *AND_OR), true);
|
||||
eval_test(true, &g, &e, &format!("{} (and false true)", *AND_OR), false);
|
||||
eval_test(true, &g, &e, &format!("{} (and true false)", *AND_OR), false);
|
||||
eval_test(true, &g, &e, &format!("{} (and false false)", *AND_OR), false);
|
||||
|
||||
eval_test(true, &g, &e, &format!("{} (or true true)", *AND_OR), true);
|
||||
eval_test(true, &g, &e, &format!("{} (or false true)", *AND_OR), true);
|
||||
eval_test(true, &g, &e, &format!("{} (or true false)", *AND_OR), true);
|
||||
eval_test(true, &g, &e, &format!("{} (or false false)", *AND_OR), false);
|
||||
}
|
||||
static LEN: Lazy<String> = Lazy::new(|| {
|
||||
format!("
|
||||
{}
|
||||
!(let1 len (lambda (l)
|
||||
!(let1 len_helper (rlambda len_helper (l a)
|
||||
(if (pair? l) (len_helper (cdr l) (+ 1 a))
|
||||
a)
|
||||
))
|
||||
(len_helper l 0)
|
||||
))
|
||||
", *AND_OR)
|
||||
});
|
||||
|
||||
#[test]
|
||||
fn len_eval_test() { let g = grammar::TermParser::new(); let e = root_env();
|
||||
eval_test(true, &g, &e, &format!("{} (len '())", *LEN), 0);
|
||||
eval_test(true, &g, &e, &format!("{} (len '(1))", *LEN), 1);
|
||||
eval_test(true, &g, &e, &format!("{} (len '(1 2))", *LEN), 2);
|
||||
eval_test(true, &g, &e, &format!("{} (len '(1 2 3))", *LEN), 3);
|
||||
}
|
||||
static MATCH: Lazy<String> = Lazy::new(|| {
|
||||
format!("
|
||||
{}
|
||||
!(let1 match (bvau de (x . cases)
|
||||
!(let1 evaluate_case (rlambda evaluate_case (access c)
|
||||
!(if (symbol? c) (list true (lambda (b) (list let1 c access b))))
|
||||
!(if (and (pair? c) (= 'unquote (car c))) (list (list = access (car (cdr c))) (lambda (b) b)))
|
||||
!(if (and (pair? c) (= 'quote (car c))) (list (list = access c) (lambda (b) b)))
|
||||
!(if (pair? c)
|
||||
!(let1 tests (list and (list pair? access) (list = (len c) (list len access))))
|
||||
!(let1 (tests body_func) ((rlambda recurse (c tests access body_func) (if (pair? c)
|
||||
!(let1 (inner_test inner_body_func) (evaluate_case (list car access) (car c)))
|
||||
(recurse (cdr c)
|
||||
(list and tests inner_test)
|
||||
(list cdr access)
|
||||
(lambda (b) (body_func (inner_body_func b))))
|
||||
; else
|
||||
(list tests body_func)
|
||||
))
|
||||
c tests access (lambda (b) b)))
|
||||
(list tests body_func))
|
||||
(list (list = access c) (lambda (b) b))
|
||||
))
|
||||
!(let1 helper (rlambda helper (x_sym cases) (if (= nil cases) (list assert false)
|
||||
(let1 (test body_func) (evaluate_case x_sym (car cases))
|
||||
(concat (list if test (body_func (car (cdr cases)))) (list (helper x_sym (cdr (cdr cases)))))))))
|
||||
|
||||
(eval (list let1 '___MATCH_SYM x (helper '___MATCH_SYM cases)) de)
|
||||
;!(let1 expanded (list let1 '___MATCH_SYM x (helper '___MATCH_SYM cases)))
|
||||
;(debug expanded (eval expanded de))
|
||||
))
|
||||
", *LEN)
|
||||
});
|
||||
#[test]
|
||||
fn match_eval_test() { let g = grammar::TermParser::new(); let e = root_env();
|
||||
eval_test(true, &g, &e, &format!("{} (match (+ 1 2) 1 2 2 3 3 4 _ 0)", *MATCH), 4);
|
||||
eval_test(true, &g, &e, &format!("{} (match '(1 2) 1 2 2 3 3 4 _ 0)", *MATCH), 0);
|
||||
eval_test(true, &g, &e, &format!("{} (match '(1 2) 1 2 2 3 (a b) (+ a (+ 2 b)) _ 0)", *MATCH), 5);
|
||||
eval_test(true, &g, &e, &format!("{} (match '(1 2) 1 2 2 3 '(1 2) 7 _ 0)", *MATCH), 7);
|
||||
eval_test(true, &g, &e, &format!("{} (let1 a 70 (match (+ 60 10) (unquote a) 100 2 3 _ 0))", *MATCH), 100);
|
||||
}
|
||||
static RBTREE: Lazy<String> = Lazy::new(|| {
|
||||
format!("
|
||||
{}
|
||||
!(let1 empty (list 'B nil nil nil))
|
||||
!(let1 E empty)
|
||||
!(let1 EE (list 'BB nil nil nil))
|
||||
|
||||
!(let1 generic-foldl (rlambda generic-foldl (f z t) (match t
|
||||
(unquote E) z
|
||||
|
||||
(c a x b) !(let1 new_left_result (generic-foldl f z a))
|
||||
!(let1 folded (f new_left_result x))
|
||||
(generic-foldl f folded b))))
|
||||
|
||||
!(let1 blacken (lambda (t) (match t
|
||||
('R a x b) (list 'B a x b)
|
||||
t t)))
|
||||
!(let1 balance (lambda (t) (match t
|
||||
; figures 1 and 2
|
||||
('B ('R ('R a x b) y c) z d) (list 'R (list 'B a x b) y (list 'B c z d))
|
||||
('B ('R a x ('R b y c)) z d) (list 'R (list 'B a x b) y (list 'B c z d))
|
||||
('B a x ('R ('R b y c) z d)) (list 'R (list 'B a x b) y (list 'B c z d))
|
||||
('B a x ('R b y ('R c z d))) (list 'R (list 'B a x b) y (list 'B c z d))
|
||||
; figure 8, double black cases
|
||||
('BB ('R a x ('R b y c)) z d) (list 'B (list 'B a x b) y (list 'B c z d))
|
||||
('BB a x ('R ('R b y c) z d)) (list 'B (list 'B a x b) y (list 'B c z d))
|
||||
; already balenced
|
||||
t t)))
|
||||
|
||||
!(let1 map-insert !(let1 ins (rlambda ins (t k v) (match t
|
||||
(unquote E) (list 'R t (list k v) t)
|
||||
(c a x b) !(if (< k (car x)) (balance (list c (ins a k v) x b)))
|
||||
!(if (= k (car x)) (list c a (list k v) b))
|
||||
(balance (list c a x (ins b k v))))))
|
||||
(lambda (t k v) (blacken (ins t k v))))
|
||||
|
||||
!(let1 map-empty empty)
|
||||
|
||||
!(let1 make-test-tree (rlambda make-test-tree (n t) (if (<= n 0) t
|
||||
(make-test-tree (- n 1) (map-insert t n (= 0 (% n 10)))))))
|
||||
!(let1 reduce-test-tree (lambda (tree) (generic-foldl (lambda (a x) (if (car (cdr x)) (+ a 1) a)) 0 tree)))
|
||||
", *MATCH)
|
||||
});
|
||||
#[test]
|
||||
fn rbtree_eval_test() { let g = grammar::TermParser::new(); let e = root_env();
|
||||
eval_test(false, &g, &e, &format!("{} (reduce-test-tree (make-test-tree 10 map-empty))", *RBTREE), 1);
|
||||
//eval_test(false, &g, &e, &format!("{} (reduce-test-tree (make-test-tree 20 map-empty))", *RBTREE), 2);
|
||||
}
|
||||
163
website/LICENSE
Normal file
163
website/LICENSE
Normal file
@@ -0,0 +1,163 @@
|
||||
###########################
|
||||
For Recursive (the font):
|
||||
###########################
|
||||
|
||||
Copyright 2020 The Recursive Project Authors (https://github.com/arrowtype/recursive)
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
http://scripts.sil.org/OFL
|
||||
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
|
||||
|
||||
|
||||
|
||||
###########################
|
||||
For CodeJar:
|
||||
###########################
|
||||
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020 Anton Medvedev
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
|
||||
|
||||
|
||||
###########################
|
||||
For highlight.js:
|
||||
###########################
|
||||
|
||||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2006, Ivan Sagalaev.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
BIN
website/Recursive.woff2
Normal file
BIN
website/Recursive.woff2
Normal file
Binary file not shown.
466
website/codejar.js
Normal file
466
website/codejar.js
Normal file
@@ -0,0 +1,466 @@
|
||||
const globalWindow = window;
|
||||
export function CodeJar(editor, highlight, opt = {}) {
|
||||
const options = Object.assign({ tab: '\t', indentOn: /[({\[]$/, moveToNewLine: /^[)}\]]/, spellcheck: false, catchTab: true, preserveIdent: true, addClosing: true, history: true, window: globalWindow }, opt);
|
||||
const window = options.window;
|
||||
const document = window.document;
|
||||
let listeners = [];
|
||||
let history = [];
|
||||
let at = -1;
|
||||
let focus = false;
|
||||
let callback;
|
||||
let prev; // code content prior keydown event
|
||||
editor.setAttribute('contenteditable', 'plaintext-only');
|
||||
editor.setAttribute('spellcheck', options.spellcheck ? 'true' : 'false');
|
||||
editor.style.outline = 'none';
|
||||
editor.style.overflowWrap = 'break-word';
|
||||
editor.style.overflowY = 'auto';
|
||||
editor.style.whiteSpace = 'pre-wrap';
|
||||
let isLegacy = false; // true if plaintext-only is not supported
|
||||
highlight(editor);
|
||||
if (editor.contentEditable !== 'plaintext-only')
|
||||
isLegacy = true;
|
||||
if (isLegacy)
|
||||
editor.setAttribute('contenteditable', 'true');
|
||||
const debounceHighlight = debounce(() => {
|
||||
const pos = save();
|
||||
highlight(editor, pos);
|
||||
restore(pos);
|
||||
}, 30);
|
||||
let recording = false;
|
||||
const shouldRecord = (event) => {
|
||||
return !isUndo(event) && !isRedo(event)
|
||||
&& event.key !== 'Meta'
|
||||
&& event.key !== 'Control'
|
||||
&& event.key !== 'Alt'
|
||||
&& !event.key.startsWith('Arrow');
|
||||
};
|
||||
const debounceRecordHistory = debounce((event) => {
|
||||
if (shouldRecord(event)) {
|
||||
recordHistory();
|
||||
recording = false;
|
||||
}
|
||||
}, 300);
|
||||
const on = (type, fn) => {
|
||||
listeners.push([type, fn]);
|
||||
editor.addEventListener(type, fn);
|
||||
};
|
||||
on('keydown', event => {
|
||||
if (event.defaultPrevented)
|
||||
return;
|
||||
prev = toString();
|
||||
if (options.preserveIdent)
|
||||
handleNewLine(event);
|
||||
else
|
||||
legacyNewLineFix(event);
|
||||
if (options.catchTab)
|
||||
handleTabCharacters(event);
|
||||
if (options.addClosing)
|
||||
handleSelfClosingCharacters(event);
|
||||
if (options.history) {
|
||||
handleUndoRedo(event);
|
||||
if (shouldRecord(event) && !recording) {
|
||||
recordHistory();
|
||||
recording = true;
|
||||
}
|
||||
}
|
||||
if (isLegacy && !isCopy(event))
|
||||
restore(save());
|
||||
});
|
||||
on('keyup', event => {
|
||||
if (event.defaultPrevented)
|
||||
return;
|
||||
if (event.isComposing)
|
||||
return;
|
||||
if (prev !== toString())
|
||||
debounceHighlight();
|
||||
debounceRecordHistory(event);
|
||||
if (callback)
|
||||
callback(toString());
|
||||
});
|
||||
on('focus', _event => {
|
||||
focus = true;
|
||||
});
|
||||
on('blur', _event => {
|
||||
focus = false;
|
||||
});
|
||||
on('paste', event => {
|
||||
recordHistory();
|
||||
handlePaste(event);
|
||||
recordHistory();
|
||||
if (callback)
|
||||
callback(toString());
|
||||
});
|
||||
function save() {
|
||||
const s = getSelection();
|
||||
const pos = { start: 0, end: 0, dir: undefined };
|
||||
let { anchorNode, anchorOffset, focusNode, focusOffset } = s;
|
||||
if (!anchorNode || !focusNode)
|
||||
throw 'error1';
|
||||
// If the anchor and focus are the editor element, return either a full
|
||||
// highlight or a start/end cursor position depending on the selection
|
||||
if (anchorNode === editor && focusNode === editor) {
|
||||
pos.start = (anchorOffset > 0 && editor.textContent) ? editor.textContent.length : 0;
|
||||
pos.end = (focusOffset > 0 && editor.textContent) ? editor.textContent.length : 0;
|
||||
pos.dir = (focusOffset >= anchorOffset) ? '->' : '<-';
|
||||
return pos;
|
||||
}
|
||||
// Selection anchor and focus are expected to be text nodes,
|
||||
// so normalize them.
|
||||
if (anchorNode.nodeType === Node.ELEMENT_NODE) {
|
||||
const node = document.createTextNode('');
|
||||
anchorNode.insertBefore(node, anchorNode.childNodes[anchorOffset]);
|
||||
anchorNode = node;
|
||||
anchorOffset = 0;
|
||||
}
|
||||
if (focusNode.nodeType === Node.ELEMENT_NODE) {
|
||||
const node = document.createTextNode('');
|
||||
focusNode.insertBefore(node, focusNode.childNodes[focusOffset]);
|
||||
focusNode = node;
|
||||
focusOffset = 0;
|
||||
}
|
||||
visit(editor, el => {
|
||||
if (el === anchorNode && el === focusNode) {
|
||||
pos.start += anchorOffset;
|
||||
pos.end += focusOffset;
|
||||
pos.dir = anchorOffset <= focusOffset ? '->' : '<-';
|
||||
return 'stop';
|
||||
}
|
||||
if (el === anchorNode) {
|
||||
pos.start += anchorOffset;
|
||||
if (!pos.dir) {
|
||||
pos.dir = '->';
|
||||
}
|
||||
else {
|
||||
return 'stop';
|
||||
}
|
||||
}
|
||||
else if (el === focusNode) {
|
||||
pos.end += focusOffset;
|
||||
if (!pos.dir) {
|
||||
pos.dir = '<-';
|
||||
}
|
||||
else {
|
||||
return 'stop';
|
||||
}
|
||||
}
|
||||
if (el.nodeType === Node.TEXT_NODE) {
|
||||
if (pos.dir != '->')
|
||||
pos.start += el.nodeValue.length;
|
||||
if (pos.dir != '<-')
|
||||
pos.end += el.nodeValue.length;
|
||||
}
|
||||
});
|
||||
// collapse empty text nodes
|
||||
editor.normalize();
|
||||
return pos;
|
||||
}
|
||||
function restore(pos) {
|
||||
const s = getSelection();
|
||||
let startNode, startOffset = 0;
|
||||
let endNode, endOffset = 0;
|
||||
if (!pos.dir)
|
||||
pos.dir = '->';
|
||||
if (pos.start < 0)
|
||||
pos.start = 0;
|
||||
if (pos.end < 0)
|
||||
pos.end = 0;
|
||||
// Flip start and end if the direction reversed
|
||||
if (pos.dir == '<-') {
|
||||
const { start, end } = pos;
|
||||
pos.start = end;
|
||||
pos.end = start;
|
||||
}
|
||||
let current = 0;
|
||||
visit(editor, el => {
|
||||
if (el.nodeType !== Node.TEXT_NODE)
|
||||
return;
|
||||
const len = (el.nodeValue || '').length;
|
||||
if (current + len > pos.start) {
|
||||
if (!startNode) {
|
||||
startNode = el;
|
||||
startOffset = pos.start - current;
|
||||
}
|
||||
if (current + len > pos.end) {
|
||||
endNode = el;
|
||||
endOffset = pos.end - current;
|
||||
return 'stop';
|
||||
}
|
||||
}
|
||||
current += len;
|
||||
});
|
||||
if (!startNode)
|
||||
startNode = editor, startOffset = editor.childNodes.length;
|
||||
if (!endNode)
|
||||
endNode = editor, endOffset = editor.childNodes.length;
|
||||
// Flip back the selection
|
||||
if (pos.dir == '<-') {
|
||||
[startNode, startOffset, endNode, endOffset] = [endNode, endOffset, startNode, startOffset];
|
||||
}
|
||||
s.setBaseAndExtent(startNode, startOffset, endNode, endOffset);
|
||||
}
|
||||
function beforeCursor() {
|
||||
const s = getSelection();
|
||||
const r0 = s.getRangeAt(0);
|
||||
const r = document.createRange();
|
||||
r.selectNodeContents(editor);
|
||||
r.setEnd(r0.startContainer, r0.startOffset);
|
||||
return r.toString();
|
||||
}
|
||||
function afterCursor() {
|
||||
const s = getSelection();
|
||||
const r0 = s.getRangeAt(0);
|
||||
const r = document.createRange();
|
||||
r.selectNodeContents(editor);
|
||||
r.setStart(r0.endContainer, r0.endOffset);
|
||||
return r.toString();
|
||||
}
|
||||
function handleNewLine(event) {
|
||||
if (event.key === 'Enter') {
|
||||
const before = beforeCursor();
|
||||
const after = afterCursor();
|
||||
let [padding] = findPadding(before);
|
||||
let newLinePadding = padding;
|
||||
// If last symbol is "{" ident new line
|
||||
if (options.indentOn.test(before)) {
|
||||
newLinePadding += options.tab;
|
||||
}
|
||||
// Preserve padding
|
||||
if (newLinePadding.length > 0) {
|
||||
preventDefault(event);
|
||||
event.stopPropagation();
|
||||
insert('\n' + newLinePadding);
|
||||
}
|
||||
else {
|
||||
legacyNewLineFix(event);
|
||||
}
|
||||
// Place adjacent "}" on next line
|
||||
if (newLinePadding !== padding && options.moveToNewLine.test(after)) {
|
||||
const pos = save();
|
||||
insert('\n' + padding);
|
||||
restore(pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
function legacyNewLineFix(event) {
|
||||
// Firefox does not support plaintext-only mode
|
||||
// and puts <div><br></div> on Enter. Let's help.
|
||||
if (isLegacy && event.key === 'Enter') {
|
||||
preventDefault(event);
|
||||
event.stopPropagation();
|
||||
if (afterCursor() == '') {
|
||||
insert('\n ');
|
||||
const pos = save();
|
||||
pos.start = --pos.end;
|
||||
restore(pos);
|
||||
}
|
||||
else {
|
||||
insert('\n');
|
||||
}
|
||||
}
|
||||
}
|
||||
function handleSelfClosingCharacters(event) {
|
||||
const open = `([{'"`;
|
||||
const close = `)]}'"`;
|
||||
const codeAfter = afterCursor();
|
||||
const codeBefore = beforeCursor();
|
||||
const escapeCharacter = codeBefore.substr(codeBefore.length - 1) === '\\';
|
||||
const charAfter = codeAfter.substr(0, 1);
|
||||
if (close.includes(event.key) && !escapeCharacter && charAfter === event.key) {
|
||||
// We already have closing char next to cursor.
|
||||
// Move one char to right.
|
||||
const pos = save();
|
||||
preventDefault(event);
|
||||
pos.start = ++pos.end;
|
||||
restore(pos);
|
||||
}
|
||||
else if (open.includes(event.key)
|
||||
&& !escapeCharacter
|
||||
&& (`"'`.includes(event.key) || ['', ' ', '\n'].includes(charAfter))) {
|
||||
preventDefault(event);
|
||||
const pos = save();
|
||||
const wrapText = pos.start == pos.end ? '' : getSelection().toString();
|
||||
const text = event.key + wrapText + close[open.indexOf(event.key)];
|
||||
insert(text);
|
||||
pos.start++;
|
||||
pos.end++;
|
||||
restore(pos);
|
||||
}
|
||||
}
|
||||
function handleTabCharacters(event) {
|
||||
if (event.key === 'Tab') {
|
||||
preventDefault(event);
|
||||
if (event.shiftKey) {
|
||||
const before = beforeCursor();
|
||||
let [padding, start,] = findPadding(before);
|
||||
if (padding.length > 0) {
|
||||
const pos = save();
|
||||
// Remove full length tab or just remaining padding
|
||||
const len = Math.min(options.tab.length, padding.length);
|
||||
restore({ start, end: start + len });
|
||||
document.execCommand('delete');
|
||||
pos.start -= len;
|
||||
pos.end -= len;
|
||||
restore(pos);
|
||||
}
|
||||
}
|
||||
else {
|
||||
insert(options.tab);
|
||||
}
|
||||
}
|
||||
}
|
||||
function handleUndoRedo(event) {
|
||||
if (isUndo(event)) {
|
||||
preventDefault(event);
|
||||
at--;
|
||||
const record = history[at];
|
||||
if (record) {
|
||||
editor.innerHTML = record.html;
|
||||
restore(record.pos);
|
||||
}
|
||||
if (at < 0)
|
||||
at = 0;
|
||||
}
|
||||
if (isRedo(event)) {
|
||||
preventDefault(event);
|
||||
at++;
|
||||
const record = history[at];
|
||||
if (record) {
|
||||
editor.innerHTML = record.html;
|
||||
restore(record.pos);
|
||||
}
|
||||
if (at >= history.length)
|
||||
at--;
|
||||
}
|
||||
}
|
||||
function recordHistory() {
|
||||
if (!focus)
|
||||
return;
|
||||
const html = editor.innerHTML;
|
||||
const pos = save();
|
||||
const lastRecord = history[at];
|
||||
if (lastRecord) {
|
||||
if (lastRecord.html === html
|
||||
&& lastRecord.pos.start === pos.start
|
||||
&& lastRecord.pos.end === pos.end)
|
||||
return;
|
||||
}
|
||||
at++;
|
||||
history[at] = { html, pos };
|
||||
history.splice(at + 1);
|
||||
const maxHistory = 300;
|
||||
if (at > maxHistory) {
|
||||
at = maxHistory;
|
||||
history.splice(0, 1);
|
||||
}
|
||||
}
|
||||
function handlePaste(event) {
|
||||
preventDefault(event);
|
||||
const text = (event.originalEvent || event)
|
||||
.clipboardData
|
||||
.getData('text/plain')
|
||||
.replace(/\r/g, '');
|
||||
const pos = save();
|
||||
insert(text);
|
||||
highlight(editor);
|
||||
restore({
|
||||
start: Math.min(pos.start, pos.end) + text.length,
|
||||
end: Math.min(pos.start, pos.end) + text.length,
|
||||
dir: '<-',
|
||||
});
|
||||
}
|
||||
function visit(editor, visitor) {
|
||||
const queue = [];
|
||||
if (editor.firstChild)
|
||||
queue.push(editor.firstChild);
|
||||
let el = queue.pop();
|
||||
while (el) {
|
||||
if (visitor(el) === 'stop')
|
||||
break;
|
||||
if (el.nextSibling)
|
||||
queue.push(el.nextSibling);
|
||||
if (el.firstChild)
|
||||
queue.push(el.firstChild);
|
||||
el = queue.pop();
|
||||
}
|
||||
}
|
||||
function isCtrl(event) {
|
||||
return event.metaKey || event.ctrlKey;
|
||||
}
|
||||
function isUndo(event) {
|
||||
return isCtrl(event) && !event.shiftKey && getKeyCode(event) === 'Z';
|
||||
}
|
||||
function isRedo(event) {
|
||||
return isCtrl(event) && event.shiftKey && getKeyCode(event) === 'Z';
|
||||
}
|
||||
function isCopy(event) {
|
||||
return isCtrl(event) && getKeyCode(event) === 'C';
|
||||
}
|
||||
function getKeyCode(event) {
|
||||
let key = event.key || event.keyCode || event.which;
|
||||
if (!key)
|
||||
return undefined;
|
||||
return (typeof key === 'string' ? key : String.fromCharCode(key)).toUpperCase();
|
||||
}
|
||||
function insert(text) {
|
||||
text = text
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''');
|
||||
document.execCommand('insertHTML', false, text);
|
||||
}
|
||||
function debounce(cb, wait) {
|
||||
let timeout = 0;
|
||||
return (...args) => {
|
||||
clearTimeout(timeout);
|
||||
timeout = window.setTimeout(() => cb(...args), wait);
|
||||
};
|
||||
}
|
||||
function findPadding(text) {
|
||||
// Find beginning of previous line.
|
||||
let i = text.length - 1;
|
||||
while (i >= 0 && text[i] !== '\n')
|
||||
i--;
|
||||
i++;
|
||||
// Find padding of the line.
|
||||
let j = i;
|
||||
while (j < text.length && /[ \t]/.test(text[j]))
|
||||
j++;
|
||||
return [text.substring(i, j) || '', i, j];
|
||||
}
|
||||
function toString() {
|
||||
return editor.textContent || '';
|
||||
}
|
||||
function preventDefault(event) {
|
||||
event.preventDefault();
|
||||
}
|
||||
function getSelection() {
|
||||
var _a;
|
||||
if (((_a = editor.parentNode) === null || _a === void 0 ? void 0 : _a.nodeType) == Node.DOCUMENT_FRAGMENT_NODE) {
|
||||
return editor.parentNode.getSelection();
|
||||
}
|
||||
return window.getSelection();
|
||||
}
|
||||
return {
|
||||
updateOptions(newOptions) {
|
||||
Object.assign(options, newOptions);
|
||||
},
|
||||
updateCode(code) {
|
||||
editor.textContent = code;
|
||||
highlight(editor);
|
||||
},
|
||||
onUpdate(cb) {
|
||||
callback = cb;
|
||||
},
|
||||
toString,
|
||||
save,
|
||||
restore,
|
||||
recordHistory,
|
||||
destroy() {
|
||||
for (let [type, fn] of listeners) {
|
||||
editor.removeEventListener(type, fn);
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
9
website/default.min.css
vendored
Normal file
9
website/default.min.css
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
/*!
|
||||
Theme: Default
|
||||
Description: Original highlight.js style
|
||||
Author: (c) Ivan Sagalaev <maniac@softwaremaniacs.org>
|
||||
Maintainer: @highlightjs/core-team
|
||||
Website: https://highlightjs.org/
|
||||
License: see project LICENSE
|
||||
Touched: 2021
|
||||
*/pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{background:#f3f3f3;color:#444}.hljs-comment{color:#697070}.hljs-punctuation,.hljs-tag{color:#444a}.hljs-tag .hljs-attr,.hljs-tag .hljs-name{color:#444}.hljs-attribute,.hljs-doctag,.hljs-keyword,.hljs-meta .hljs-keyword,.hljs-name,.hljs-selector-tag{font-weight:700}.hljs-deletion,.hljs-number,.hljs-quote,.hljs-selector-class,.hljs-selector-id,.hljs-string,.hljs-template-tag,.hljs-type{color:#800}.hljs-section,.hljs-title{color:#800;font-weight:700}.hljs-link,.hljs-operator,.hljs-regexp,.hljs-selector-attr,.hljs-selector-pseudo,.hljs-symbol,.hljs-template-variable,.hljs-variable{color:#ab5656}.hljs-literal{color:#695}.hljs-addition,.hljs-built_in,.hljs-bullet,.hljs-code{color:#397300}.hljs-meta{color:#1f7199}.hljs-meta .hljs-string{color:#38a}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}
|
||||
BIN
website/favicon.ico
Normal file
BIN
website/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.2 KiB |
1202
website/highlight.min.js
vendored
Normal file
1202
website/highlight.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
171
website/index.html
Normal file
171
website/index.html
Normal file
@@ -0,0 +1,171 @@
|
||||
<!doctype html>
|
||||
<html lang="en-us">
|
||||
<meta charset="UTF-8">
|
||||
<head>
|
||||
<style>
|
||||
@font-face {
|
||||
font-family: 'Recursive';
|
||||
font-style: oblique 0deg 15deg;
|
||||
font-weight: 300 1000;
|
||||
font-display: swap;
|
||||
src: url(./Recursive.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
:root {
|
||||
--rec-wght: 400;
|
||||
--rec-slnt: 0;
|
||||
--rec-mono: 0;
|
||||
--rec-casl: 0;
|
||||
--rec-csrv: 0;
|
||||
}
|
||||
* {
|
||||
font-variation-settings: "wght" var(--rec-wght),
|
||||
"slnt" var(--rec-slnt),
|
||||
"MONO" var(--rec-mono),
|
||||
"CASL" var(--rec-casl),
|
||||
"CRSV" var(--rec-csrv);
|
||||
}
|
||||
body {
|
||||
max-width: 45em;
|
||||
margin: 1em auto;
|
||||
padding: 0 .62em;
|
||||
font: 1.2em/1.62 'Recursive', sans-serif;
|
||||
}
|
||||
h1, h2, h3, h4 {
|
||||
line-height:1.2;
|
||||
--rec-wght: 700;
|
||||
--rec-casl: 1;
|
||||
--rec-crsv: 1;
|
||||
}
|
||||
h1 {
|
||||
line-height:0.7;
|
||||
font-size: 4em;
|
||||
--rec-wght: 900;
|
||||
--rec-slnt: -15;
|
||||
text-decoration: underline;
|
||||
text-decoration-thickness: 0.4rem;
|
||||
//border-bottom: 0.08em solid;
|
||||
//border-left: 0.1em solid;
|
||||
//display: inline-block;
|
||||
}
|
||||
h2 { font-size: 3em; }
|
||||
h3 { font-size: 1.5em; }
|
||||
h4 { font-size: 1.2em; }
|
||||
i { --rec-slnt: -14; }
|
||||
b { --rec-wght: 600; }
|
||||
.run_container { position: relative; }
|
||||
.editor {
|
||||
font-family: 'Recursive', monospace;
|
||||
font-size: 1rem;
|
||||
--rec-mono: 1;
|
||||
border-radius: 6px;
|
||||
box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 1px 5px 0 rgba(0, 0, 0, 0.12), 0 3px 1px -2px rgba(0, 0, 0, 0.2);
|
||||
height: 7em;
|
||||
letter-spacing: normal;
|
||||
tab-size: 4;
|
||||
}
|
||||
.output {
|
||||
margin-block-start: 1rem;
|
||||
font-family: 'Recursive', monospace;
|
||||
font-size: 1rem;
|
||||
--rec-mono: 1;
|
||||
tab-size: 4;
|
||||
height: 5em;
|
||||
width: 100%;
|
||||
}
|
||||
.run_button {
|
||||
font-family: 'Recursive', sans-serif;
|
||||
font-size: 1em;
|
||||
--rec-wght: 900;
|
||||
--rec-slnt: -15;
|
||||
--rec-casl: 1;
|
||||
--rec-crsv: 1;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<header><h1>Kraken</h1></header>
|
||||
<i>FOSS Fexprs: <a title="Kraken on GitHub" href="https://github.com/limvot/kraken">https://github.com/limvot/kraken</a></i>
|
||||
<br>
|
||||
<h3>Demo:</h3>
|
||||
<div class="run_container">
|
||||
<div class="editor" id="hello_editor">; Of course
|
||||
(println "Hello World")
|
||||
; Just print 3
|
||||
(println "Math works:" (+ 1 2))
|
||||
</div>
|
||||
<textarea class="output" id="hello_output">Output will appear here</textarea>
|
||||
<button class="run_button" onclick="executeKraken(hello_editor_jar.toString(), 'hello_output')"><b>Run</b></button> <br>
|
||||
</div>
|
||||
<a name="concept"/>
|
||||
<h3>Concept:</h3>
|
||||
<ul>
|
||||
<li> Minimal, close to the metal Kernel/Scheme (operate on words, bytes, arrays) as AST / core language, with Kernel/Vau calculus inspiration oblivating the need for non-reader macros (<a title="Kernel/Vau calculus thesis" href="https://web.wpi.edu/Pubs/ETD/Available/etd-090110-124904/unrestricted/jshutt.pdf">Kernel/Vau calculus thesis</a>)
|
||||
<li> Full Context-free (and eventually, context sensitive) reader macros using FUN-GLL (<a title="fun-gll paper" href="https://www.sciencedirect.com/science/article/pii/S2590118420300058">FUN-GLL paper</a>) to extend language's syntax dynamically
|
||||
<li> Implement Type Systems as Macros (but using Vaus instead of macros) (<a title="type systems as macros paper 1" href="http://www.ccs.neu.edu/home/stchang/pubs/ckg-popl2017.pdf">paper, up to System Fω</a>) (<a title="type systems as macros paper 2" href="https://www.ccs.neu.edu/home/stchang/pubs/cbtb-popl2020.pdf">second paper, up to dependent types</a>)
|
||||
<li> Use above "type systems as vaus" to create richer language and embed entire other programming languages (syntax, semantics, and type system) for flawless interop/FFI (C, Go, Lua, JS, etc)
|
||||
<li> File is interpreted, and then if "main" exists it is compiled, spidering backwards to referenced functions and data (Allows interpreted code to do metaprogramming, dependency resolution, generate code, etc, which is then compiled)
|
||||
<li> Regionalized Value State Dependence Graph as backend-IR, enabling simpler implementations of powerful optimizations (<a title="RSVDG paper" href="https://arxiv.org/pdf/1912.05036.pdf">RSVDG paper</a>) so that embedded languages have good performance when compiled with little code
|
||||
</ul>
|
||||
<a name="about"/>
|
||||
<h3> About:</h3>
|
||||
<p> Currently, I am bootstrapping this new core Lisp out of my prior compiler for my programming language, Kraken. I have implemented the first version of the FUN-GLL algorithm and have working vaus and context-free reader macros.
|
||||
<p> The general flow is that the input files will be executed with the core Lisp interpreter, and if there is a "main" symbol defined the compiler emits C code for that function & all other functions & data that it references. In this way the language supports very powerful meta-programming at compile time, including adding syntax to the language, arbitrary computation, and importing other files, and then compiles into a static executable.
|
||||
<p> Below are a few examples of using the vau / live grammar modification / context-free reader macros to implement basic methods as well as embed the BF language into the core Lisp. The core Lisp implementation has been compiled to WebAssembly and should be able to run in your browser. Feel free to make edits and play around below.
|
||||
<br>
|
||||
Note that the current implementation is inefficient, and sometimes has problems running in phone web browsers.
|
||||
<a name="hello_example"/>
|
||||
<a name="vau_core"/>
|
||||
<h4>Vau/Kernel as simple core:</h4>
|
||||
By constructing our core language on a very simple Vau/Kernel base, we can keep the base truely tiny, and build up normal Lisp functions and programming language features in the language itself. This should help implement other programming languages concisely, and will hopefully make optimization easier and more broadly applicable.
|
||||
<br>
|
||||
Below is the current prelude that adds quoting, quasiquoting, syntax for arrays and quoting/quasiquoting, do, if, let, and even lambda itself!
|
||||
<a name="next_steps"/>
|
||||
<h3>Next Steps</h3>
|
||||
<ul>
|
||||
<li> Implement persistent functional data structures
|
||||
<ul>
|
||||
<li> Hash Array-Mapped Trie (HAMT) / Relaxed Radix Balance Tree (RRB-Tree)
|
||||
<li> Hash Map based on the above
|
||||
<li> Hash Set based on the above
|
||||
</ul>
|
||||
<li> Prototype Type Systems as Macros, may require macro system rewrite/upgrade
|
||||
<li> Sketch out Kraken language on top of core Lisp, includes basic Hindley-Milner type system implemented with Macros and above data structures
|
||||
<li> Re-self-host using functional approach in above Kraken language
|
||||
<li> Use Type System Macros to implement automatic transient creation on HAMT/RBB-Tree as an optimization
|
||||
<li> Implement RVSDG IR and develop best bang-for-buck optimizations using it
|
||||
</ul>
|
||||
|
||||
<link rel="stylesheet" href="./default.min.css">
|
||||
<script src="./highlight.min.js"></script>
|
||||
<script type="module">
|
||||
import {CodeJar} from './codejar.js'
|
||||
document.querySelectorAll('.editor').forEach((editor_div) => {
|
||||
window[editor_div.id + "_jar"] = CodeJar(editor_div, hljs.highlightElement)
|
||||
});
|
||||
</script>
|
||||
<script>
|
||||
var output_name = ""
|
||||
var Module = {
|
||||
noInitialRun: true,
|
||||
onRuntimeInitialized: () => {
|
||||
},
|
||||
print: txt => {
|
||||
document.getElementById(output_name).value += txt + "\n";
|
||||
},
|
||||
printErr: txt => {
|
||||
document.getElementById(output_name).value += "STDERR:[" + txt + "]\n";
|
||||
}
|
||||
};
|
||||
function executeKraken(code, new_output_name) {
|
||||
output_name = new_output_name
|
||||
document.getElementById(new_output_name).value = "running...\n";
|
||||
Module.callMain(["-C", code]);
|
||||
}
|
||||
</script>
|
||||
<script type="text/javascript" src="k_prime.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user