Compare commits
2 Commits
2787b95837
...
56c6b3f5f7
| Author | SHA1 | Date | |
|---|---|---|---|
| 56c6b3f5f7 | |||
| 82c38a32cf |
255
sl/src/lib.rs
255
sl/src/lib.rs
@@ -27,6 +27,11 @@ use anyhow::{anyhow,bail,Result};
|
||||
pub struct ID {
|
||||
id: i64
|
||||
}
|
||||
impl fmt::Display for ID {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.id)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Form {
|
||||
@@ -35,7 +40,7 @@ pub enum Form {
|
||||
Bool(bool),
|
||||
Symbol(String, RefCell<Option<ID>>),
|
||||
Pair(Rc<Form>, Rc<Form>, RefCell<Option<ID>>),
|
||||
Closure(Vec<String>, Rc<RefCell<Env>>, Rc<Form>, RefCell<Option<ID>>),
|
||||
Closure(Vec<String>, Rc<RefCell<Env>>, Rc<Form>, ID),
|
||||
Prim(Prim),
|
||||
}
|
||||
|
||||
@@ -55,13 +60,13 @@ pub enum Prim {
|
||||
impl Form {
|
||||
fn my_eq(&self, o: &Rc<Form>) -> bool {
|
||||
match self {
|
||||
Form::Nil => o.is_nil(),
|
||||
Form::Int(i) => if let Ok(oi) = o.int() { *i == oi } else { false },
|
||||
Form::Bool(b) => if let Ok(ob) = o.bool() { *b == ob } else { false },
|
||||
Form::Symbol(s, _id) => if let Ok(os) = o.sym() { s == os } else { false },
|
||||
Form::Pair(a,b,_id) => if let Ok((oa,ob)) = o.pair() { a.my_eq(&oa) && b.my_eq(&ob) } else { false },
|
||||
Form::Closure(_, _, _, _) => false,
|
||||
Form::Prim(p) => match &**o { Form::Prim(op) => p == op, _ => false },
|
||||
Form::Nil => o.is_nil(),
|
||||
Form::Int(i) => if let Ok(oi) = o.int() { *i == oi } else { false },
|
||||
Form::Bool(b) => if let Ok(ob) = o.bool() { *b == ob } else { false },
|
||||
Form::Symbol(s, _id) => if let Ok(os) = o.sym() { s == os } else { false },
|
||||
Form::Pair(a,b,_id) => if let Ok((oa,ob)) = o.pair() { a.my_eq(&oa) && b.my_eq(&ob) } else { false },
|
||||
Form::Closure(_, _, _, _) => false,
|
||||
Form::Prim(p) => match &**o { Form::Prim(op) => p == op, _ => false },
|
||||
}
|
||||
}
|
||||
fn new_pair(car: Rc<Form>, cdr: Rc<Form>) -> Rc<Form> {
|
||||
@@ -76,8 +81,8 @@ impl Form {
|
||||
fn new_bool(b: bool) -> Rc<Form> {
|
||||
Rc::new(Form::Bool(b))
|
||||
}
|
||||
fn new_closure(params: Vec<String>, env: Rc<RefCell<Env>>, body: Rc<Form>) -> Rc<Form> {
|
||||
Rc::new(Form::Closure(params, env, body, RefCell::new(None)))
|
||||
fn new_closure(params: Vec<String>, env: Rc<RefCell<Env>>, body: Rc<Form>, ctx: &mut Ctx) -> Rc<Form> {
|
||||
Rc::new(Form::Closure(params, env, body, ctx.alloc_id()))
|
||||
}
|
||||
fn truthy(&self) -> bool {
|
||||
match self {
|
||||
@@ -156,16 +161,16 @@ impl Env {
|
||||
Rc::new(RefCell::new(Env {
|
||||
u: None,
|
||||
m: [
|
||||
("+", Rc::new(Form::Prim(Prim::Add))),
|
||||
("-", Rc::new(Form::Prim(Prim::Sub))),
|
||||
("*", Rc::new(Form::Prim(Prim::Mul))),
|
||||
("/", Rc::new(Form::Prim(Prim::Div))),
|
||||
("%", Rc::new(Form::Prim(Prim::Mod))),
|
||||
("+", Rc::new(Form::Prim(Prim::Add))),
|
||||
("-", Rc::new(Form::Prim(Prim::Sub))),
|
||||
("*", Rc::new(Form::Prim(Prim::Mul))),
|
||||
("/", Rc::new(Form::Prim(Prim::Div))),
|
||||
("%", Rc::new(Form::Prim(Prim::Mod))),
|
||||
("cons", Rc::new(Form::Prim(Prim::Cons))),
|
||||
("cdr", Rc::new(Form::Prim(Prim::Cdr))),
|
||||
("car", Rc::new(Form::Prim(Prim::Car))),
|
||||
("=", Rc::new(Form::Prim(Prim::Eq))),
|
||||
("nil", Form::new_nil()),
|
||||
("cdr", Rc::new(Form::Prim(Prim::Cdr))),
|
||||
("car", Rc::new(Form::Prim(Prim::Car))),
|
||||
("=", Rc::new(Form::Prim(Prim::Eq))),
|
||||
("nil", Form::new_nil()),
|
||||
].into_iter().map(|(s,p)| (s.to_owned(), p)).collect()
|
||||
}))
|
||||
}
|
||||
@@ -191,13 +196,40 @@ impl Env {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Op {
|
||||
Guard { const_value: Rc<Form>, side: (Option<Rc<Form>>, Rc<Cont>) },
|
||||
Debug,
|
||||
Define { sym: String },
|
||||
}
|
||||
impl fmt::Display for Op {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Op::Guard { const_value, side } => write!(f, "Guard"),
|
||||
Op::Debug => write!(f, "Debug"),
|
||||
Op::Define { sym } => write!(f, "Define {sym}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
#[derive(Debug)]
|
||||
struct Trace {
|
||||
id: ID,
|
||||
// needs to track which are constants
|
||||
ops: Vec<Op>,
|
||||
}
|
||||
impl Trace {
|
||||
fn new(id: ID) -> Self {
|
||||
Trace { id }
|
||||
Trace { id, ops: vec![] }
|
||||
}
|
||||
}
|
||||
impl fmt::Display for Trace {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "Trace for {} [", self.id)?;
|
||||
for op in &self.ops {
|
||||
write!(f, " {}", op)?;
|
||||
}
|
||||
write!(f, " ]")?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -206,6 +238,12 @@ struct Ctx {
|
||||
id_counter: i64,
|
||||
func_calls: BTreeMap<ID, i64>,
|
||||
tracing: Option<Trace>,
|
||||
traces: BTreeMap<ID, Trace>,
|
||||
}
|
||||
impl fmt::Display for Ctx {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "Ctx")
|
||||
}
|
||||
}
|
||||
impl Ctx {
|
||||
fn new() -> Ctx {
|
||||
@@ -213,97 +251,170 @@ impl Ctx {
|
||||
id_counter: 0,
|
||||
func_calls: BTreeMap::new(),
|
||||
tracing: None,
|
||||
traces: BTreeMap::new(),
|
||||
}
|
||||
}
|
||||
fn alloc_id(&mut self) -> ID {
|
||||
self.id_counter += 1;
|
||||
ID { id: self.id_counter }
|
||||
}
|
||||
fn trace_call_start(&mut self, id: &RefCell<Option<ID>>) {
|
||||
// shenanigins for controlling the guard
|
||||
{
|
||||
if id.borrow().is_none() {
|
||||
let new_id = self.alloc_id();
|
||||
id.replace(Some(new_id));
|
||||
}
|
||||
}
|
||||
let id = id.borrow().unwrap();
|
||||
fn trace_running(&self) -> bool { self.tracing.is_some() }
|
||||
fn trace_call_start(&mut self, id: ID) {
|
||||
|
||||
// Needs to take and use parameters for mid-trace
|
||||
// needs to guard on function called if non-constant
|
||||
|
||||
let entry = self.func_calls.entry(id).or_insert(0);
|
||||
println!("tracing call start for {id}, has been called {} times so far", *entry);
|
||||
*entry += 1;
|
||||
if *entry > 10 && self.tracing.is_none() {
|
||||
if let Some(trace) = &self.tracing {
|
||||
if trace.id == id {
|
||||
println!("Ending trace at recursive call!");
|
||||
println!("\t{}", trace);
|
||||
self.traces.insert(id, self.tracing.take().unwrap());
|
||||
}
|
||||
} else if *entry > 1 && self.traces.get(&id).is_none() {
|
||||
self.tracing = Some(Trace::new(id));
|
||||
}
|
||||
}
|
||||
fn trace_call_end(&mut self, id: &RefCell<Option<ID>>) {
|
||||
let id = { *id.borrow() };
|
||||
fn trace_call_end(&mut self, id: ID) {
|
||||
// associate with it or something
|
||||
println!("tracing call end for {id}");
|
||||
if let Some(trace) = &self.tracing {
|
||||
if trace.id == id {
|
||||
println!("Ending trace at end of call!");
|
||||
println!("\t{}", trace);
|
||||
self.traces.insert(id, self.tracing.take().unwrap());
|
||||
}
|
||||
}
|
||||
}
|
||||
fn trace_guard<T: Into<Form> + std::fmt::Debug >(&mut self, value: T, other: impl Fn()->(Option<Rc<Form>>,Rc<Cont>)) {
|
||||
println!("Tracing guard {value:?}");
|
||||
if let Some(trace) = &mut self.tracing {
|
||||
trace.ops.push(Op::Guard { const_value: Rc::new(value.into()), side: other() });
|
||||
}
|
||||
}
|
||||
fn trace_debug(&mut self) {
|
||||
if let Some(trace) = &mut self.tracing {
|
||||
trace.ops.push(Op::Debug);
|
||||
}
|
||||
}
|
||||
fn trace_define(&mut self, sym: &str) {
|
||||
if let Some(trace) = &mut self.tracing {
|
||||
trace.ops.push(Op::Define { sym: sym.to_owned() });
|
||||
}
|
||||
}
|
||||
fn trace_call_bit(&mut self) {
|
||||
if let Some(trace) = &mut self.tracing {
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
// Trace call start, of course, handles the other side!
|
||||
// Though I guess that means call start should recieve the parameters
|
||||
// also, for like variables, it should guard on what function
|
||||
// if dynamic, interacts with the constant tracking
|
||||
fn trace_prim(&mut self, p: &Prim) {
|
||||
if let Some(trace) = &mut self.tracing {
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
fn trace_lookup(&mut self, s: &str) {
|
||||
if let Some(trace) = &mut self.tracing {
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
fn trace_constant(&mut self, c: &Rc<Form>) {
|
||||
if let Some(trace) = &mut self.tracing {
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
fn trace_lambda(&mut self, params: &[String], e: &Rc<RefCell<Env>>, body: &Rc<Form>) {
|
||||
if let Some(trace) = &mut self.tracing {
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
}
|
||||
#[derive(Clone,Debug)]
|
||||
enum Cont {
|
||||
MetaRet,
|
||||
Ret { e: Rc<RefCell<Env>>, c: Box<Cont> },
|
||||
Eval { c: Box<Cont> },
|
||||
Prim { s: &'static str, to_go: Rc<Form>, c: Box<Cont> },
|
||||
Call { evaled: Vec<Rc<Form>>, to_go: Rc<Form>, c: Box<Cont> },
|
||||
Ret { e: Rc<RefCell<Env>>, id: ID, c: Rc<Cont> },
|
||||
Eval { c: Rc<Cont> },
|
||||
Prim { s: &'static str, to_go: Rc<Form>, c: Rc<Cont> },
|
||||
Call { evaled: Vec<Rc<Form>>, to_go: Rc<Form>, c: Rc<Cont> },
|
||||
}
|
||||
|
||||
pub fn eval(f: Rc<Form>) -> Result<Rc<Form>> {
|
||||
let mut ctx = Ctx::new();
|
||||
let mut f = f;
|
||||
let mut e = Env::root_env();
|
||||
let mut c = Cont::Eval { c: Box::new(Cont::MetaRet) };
|
||||
let mut c = Cont::Eval { c: Rc::new(Cont::MetaRet) };
|
||||
|
||||
loop {
|
||||
match c {
|
||||
Cont::MetaRet => {
|
||||
println!("Ctx were {ctx:?}");
|
||||
println!("Ctx was {ctx}");
|
||||
assert!(!ctx.trace_running());
|
||||
return Ok(f);
|
||||
}
|
||||
Cont::Ret { e: ne, c: nc } => {
|
||||
Cont::Ret { e: ne, id, c: nc } => {
|
||||
ctx.trace_call_end(id);
|
||||
e = ne;
|
||||
c = *nc;
|
||||
c = (*nc).clone();
|
||||
},
|
||||
Cont::Prim { s, to_go, c: nc } => {
|
||||
match s {
|
||||
"if" => {
|
||||
let thn = to_go.car()?;
|
||||
let els = to_go.cdr()?.car()?;
|
||||
if f.truthy() {
|
||||
f = to_go.car()?;
|
||||
ctx.trace_guard(true, || (Some(Rc::clone(&els)), Rc::new(Cont::Eval { c: Rc::clone(&nc) })));
|
||||
f = thn;
|
||||
} else {
|
||||
f = to_go.cdr()?.car()?;
|
||||
ctx.trace_guard(false, ||(Some(Rc::clone(&thn)), Rc::new(Cont::Eval { c: Rc::clone(&nc) })));
|
||||
f = els;
|
||||
}
|
||||
c = Cont::Eval { c: nc };
|
||||
},
|
||||
"or" => {
|
||||
let other = to_go.car()?;
|
||||
if !f.truthy() {
|
||||
f = to_go.car()?;
|
||||
ctx.trace_guard(false, || (None, nc.clone()));
|
||||
f = other;
|
||||
c = Cont::Eval { c: nc };
|
||||
} else {
|
||||
c = *nc;
|
||||
ctx.trace_guard(true, || (Some(Rc::clone(&other)), Rc::new(Cont::Eval { c: Rc::clone(&nc) })));
|
||||
c = (*nc).clone();
|
||||
}
|
||||
},
|
||||
"and" => {
|
||||
let other = to_go.car()?;
|
||||
if f.truthy() {
|
||||
f = to_go.car()?;
|
||||
ctx.trace_guard(true, || (None, nc.clone()));
|
||||
f = other;
|
||||
c = Cont::Eval { c: nc };
|
||||
} else {
|
||||
c = *nc;
|
||||
ctx.trace_guard(false, || (Some(Rc::clone(&other)), Rc::new(Cont::Eval { c: Rc::clone(&nc) })));
|
||||
c = (*nc).clone();
|
||||
}
|
||||
},
|
||||
"begin" => {
|
||||
if to_go.is_nil() {
|
||||
c = *nc;
|
||||
c = (*nc).clone();
|
||||
} else {
|
||||
f = to_go.car()?;
|
||||
c = Cont::Eval { c: Box::new(Cont::Prim { s: "begin", to_go: to_go.cdr()?, c: nc }) };
|
||||
c = Cont::Eval { c: Rc::new(Cont::Prim { s: "begin", to_go: to_go.cdr()?, c: nc }) };
|
||||
}
|
||||
},
|
||||
"debug" => {
|
||||
println!("Debug: {f}");
|
||||
c = *nc;
|
||||
ctx.trace_debug();
|
||||
c = (*nc).clone();
|
||||
},
|
||||
"define" => {
|
||||
e.borrow_mut().define(to_go.sym()?.to_string(), Rc::clone(&f));
|
||||
c = *nc;
|
||||
let sym = to_go.sym()?.to_string();
|
||||
ctx.trace_define(&sym);
|
||||
e.borrow_mut().define(sym, Rc::clone(&f));
|
||||
c = (*nc).clone();
|
||||
},
|
||||
_ => {
|
||||
panic!("bad prim {s}");
|
||||
@@ -311,6 +422,7 @@ pub fn eval(f: Rc<Form>) -> Result<Rc<Form>> {
|
||||
}
|
||||
},
|
||||
Cont::Call { mut evaled, to_go, c: nc } => {
|
||||
ctx.trace_call_bit();
|
||||
evaled.push(f);
|
||||
if to_go.is_nil() {
|
||||
// do call
|
||||
@@ -326,12 +438,13 @@ pub fn eval(f: Rc<Form>) -> Result<Rc<Form>> {
|
||||
for (name, value) in ps.iter().zip(evaled_iter) {
|
||||
new_env.borrow_mut().define(name.to_string(), value);
|
||||
}
|
||||
ctx.trace_call_start(id);
|
||||
c = Cont::Eval { c: Box::new(Cont::Ret { e: Rc::clone(&e), c: nc }) };
|
||||
ctx.trace_call_start(*id);
|
||||
c = Cont::Eval { c: Rc::new(Cont::Ret { e: Rc::clone(&e), id: *id, c: nc }) };
|
||||
f = Rc::clone(&b);
|
||||
e = new_env;
|
||||
},
|
||||
Form::Prim(p) => {
|
||||
ctx.trace_prim(p);
|
||||
let a = evaled_iter.next().unwrap();
|
||||
f = match comb.prim().unwrap() {
|
||||
Prim::Car => a.car()?,
|
||||
@@ -350,7 +463,7 @@ pub fn eval(f: Rc<Form>) -> Result<Rc<Form>> {
|
||||
}
|
||||
}
|
||||
};
|
||||
c = *nc;
|
||||
c = (*nc).clone();
|
||||
},
|
||||
_ => {
|
||||
bail!("tried to call a non-comb {}", comb)
|
||||
@@ -358,50 +471,52 @@ pub fn eval(f: Rc<Form>) -> Result<Rc<Form>> {
|
||||
}
|
||||
} else {
|
||||
f = to_go.car()?;
|
||||
c = Cont::Eval { c: Box::new(Cont::Call { evaled, to_go: to_go.cdr()?, c: nc }) };
|
||||
c = Cont::Eval { c: Rc::new(Cont::Call { evaled, to_go: to_go.cdr()?, c: nc }) };
|
||||
}
|
||||
}
|
||||
Cont::Eval { c: nc } => {
|
||||
let tmp = f;
|
||||
match &*tmp {
|
||||
Form::Symbol(s, _id) => {
|
||||
ctx.trace_lookup(s);
|
||||
f = e.borrow().lookup(s)?;
|
||||
c = *nc;
|
||||
c = (*nc).clone();
|
||||
},
|
||||
Form::Pair(car, cdr, _id) => {
|
||||
match &**car {
|
||||
Form::Symbol(s, _id) if s == "if" => {
|
||||
f = cdr.car()?;
|
||||
c = Cont::Eval { c: Box::new(Cont::Prim { s: "if", to_go: cdr.cdr()?, c: nc }) };
|
||||
c = Cont::Eval { c: Rc::new(Cont::Prim { s: "if", to_go: cdr.cdr()?, c: nc }) };
|
||||
}
|
||||
// and/or has to short-circut, so special form
|
||||
// just like Scheme (bad ;) )
|
||||
Form::Symbol(s, _id) if s == "or" => {
|
||||
f = cdr.car()?;
|
||||
c = Cont::Eval { c: Box::new(Cont::Prim { s: "or", to_go: cdr.cdr()?, c: nc }) };
|
||||
c = Cont::Eval { c: Rc::new(Cont::Prim { s: "or", to_go: cdr.cdr()?, c: nc }) };
|
||||
}
|
||||
Form::Symbol(s, _id) if s == "and" => {
|
||||
f = cdr.car()?;
|
||||
c = Cont::Eval { c: Box::new(Cont::Prim { s: "and", to_go: cdr.cdr()?, c: nc }) };
|
||||
c = Cont::Eval { c: Rc::new(Cont::Prim { s: "and", to_go: cdr.cdr()?, c: nc }) };
|
||||
}
|
||||
Form::Symbol(s, _id) if s == "begin" => {
|
||||
f = cdr.car()?;
|
||||
c = Cont::Eval { c: Box::new(Cont::Prim { s: "begin", to_go: cdr.cdr()?, c: nc }) };
|
||||
c = Cont::Eval { c: Rc::new(Cont::Prim { s: "begin", to_go: cdr.cdr()?, c: nc }) };
|
||||
}
|
||||
Form::Symbol(s, _id) if s == "debug" => {
|
||||
f = cdr.car()?;
|
||||
c = Cont::Eval { c: Box::new(Cont::Prim { s: "debug", to_go: cdr.cdr()?, c: nc }) };
|
||||
c = Cont::Eval { c: Rc::new(Cont::Prim { s: "debug", to_go: cdr.cdr()?, c: nc }) };
|
||||
}
|
||||
// This is a fast and loose ~simple lisp~, so just go for it
|
||||
// and can have convention that this is always top levelish
|
||||
Form::Symbol(s, _id) if s == "define" => {
|
||||
// note the swap, evaluating the second not the first (define a value..)
|
||||
f = cdr.cdr()?.car()?;
|
||||
c = Cont::Eval { c: Box::new(Cont::Prim { s: "define", to_go: cdr.car()?, c: nc }) };
|
||||
c = Cont::Eval { c: Rc::new(Cont::Prim { s: "define", to_go: cdr.car()?, c: nc }) };
|
||||
}
|
||||
Form::Symbol(s, _id) if s == "quote" => {
|
||||
f = cdr.car()?;
|
||||
c = *nc;
|
||||
ctx.trace_constant(&f);
|
||||
c = (*nc).clone();
|
||||
}
|
||||
// (lambda (a b) body)
|
||||
Form::Symbol(s, _id) if s == "lambda" => {
|
||||
@@ -412,19 +527,23 @@ pub fn eval(f: Rc<Form>) -> Result<Rc<Form>> {
|
||||
params = ncdr;
|
||||
}
|
||||
let body = cdr.cdr()?.car()?;
|
||||
f = Form::new_closure(params_vec, Rc::clone(&e), body);
|
||||
c = *nc;
|
||||
// Later on, the id of the closure should maybe be augmented
|
||||
// or replaced with the id of the code it was made out of?
|
||||
ctx.trace_lambda(¶ms_vec, &e, &body);
|
||||
f = Form::new_closure(params_vec, Rc::clone(&e), body, &mut ctx);
|
||||
c = (*nc).clone();
|
||||
}
|
||||
_ => {
|
||||
f = Rc::clone(car);
|
||||
c = Cont::Eval { c: Box::new(Cont::Call { evaled: vec![], to_go: Rc::clone(cdr), c: nc }) };
|
||||
c = Cont::Eval { c: Rc::new(Cont::Call { evaled: vec![], to_go: Rc::clone(cdr), c: nc }) };
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
// value, no eval
|
||||
f = tmp;
|
||||
c = *nc;
|
||||
ctx.trace_constant(&f);
|
||||
c = (*nc).clone();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -475,7 +594,7 @@ impl fmt::Display for Form {
|
||||
}
|
||||
},
|
||||
Form::Closure(params, inner_env, code, id) => {
|
||||
write!(f, "<closure {:?}>", params)
|
||||
write!(f, "<closure{} {:?}>", id, params)
|
||||
}
|
||||
Form::Prim(p) => {
|
||||
match p {
|
||||
|
||||
@@ -28,15 +28,16 @@ fn main() -> Result<()> {
|
||||
|
||||
|
||||
|
||||
;(define faft (lambda (n) (if (= n 1) 1 (+ n (faft (- n 1))))))
|
||||
;(debug 'gonna_faft_it)
|
||||
;(debug faft)
|
||||
(define faft (lambda (n) (if (= n 1) (debug 1) (+ n (faft (- n 1))))))
|
||||
(debug 'gonna_faft_it)
|
||||
(debug faft)
|
||||
(debug (faft 6))
|
||||
;(debug (faft 400))
|
||||
|
||||
(define faft2 (lambda (n a) (if (= n 1) a (faft2 (- n 1) (+ n a)))))
|
||||
(debug 'gonna_faft2_it)
|
||||
(debug faft2)
|
||||
(debug (faft2 400 1))
|
||||
;(define faft2 (lambda (n a) (if (= n 1) a (faft2 (- n 1) (+ n a)))))
|
||||
;(debug 'gonna_faft2_it)
|
||||
;(debug faft2)
|
||||
;(debug (faft2 400 1))
|
||||
|
||||
|
||||
|
||||
|
||||
155
website/index2.html
Normal file
155
website/index2.html
Normal file
@@ -0,0 +1,155 @@
|
||||
<!doctype html>
|
||||
<html lang="en-us">
|
||||
<meta charset="UTF-8">
|
||||
<head>
|
||||
<!--<link id="theme" rel="stylesheet" type="text/css" href="slick.css"/>-->
|
||||
<link id="theme" rel="stylesheet" type="text/css" href="recursive.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<!--<div class="top_spacer"></div>-->
|
||||
<!--<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')">Run</button> <br>-->
|
||||
<!--</div>-->
|
||||
|
||||
<script>
|
||||
// based on https://codepen.io/alvarotrigo/pen/ZEJgqLN
|
||||
// Thank you!
|
||||
var words = [
|
||||
`; define control flow<pre><code>(vau de (a b)
|
||||
(let ((temp (eval a de)))
|
||||
(if temp
|
||||
temp
|
||||
(eval b de))))</code></pre>`,
|
||||
'(println "Hello World")',
|
||||
'; higher-order lazy fold<br>(fold or #f (list #t #f))',
|
||||
//'Lorem ipsum dolor sit amet',
|
||||
//' consectetur adipiscing elit',
|
||||
//'sed do eiusmod tempor incididunt'
|
||||
],
|
||||
part,
|
||||
i = 0,
|
||||
offset = 0,
|
||||
len = words.length,
|
||||
forwards = true,
|
||||
skip_count = 0,
|
||||
skip_delay = 30,
|
||||
speed = 70;
|
||||
var wordflick = function () {
|
||||
setInterval(function () {
|
||||
if (forwards) {
|
||||
if (offset >= words[i].length) {
|
||||
++skip_count;
|
||||
if (skip_count == skip_delay) {
|
||||
forwards = false;
|
||||
skip_count = 0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (offset == 0) {
|
||||
forwards = true;
|
||||
i++;
|
||||
offset = 0;
|
||||
if (i >= len) {
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
part = words[i].substr(0, offset);
|
||||
if (skip_count == 0) {
|
||||
if (forwards) {
|
||||
offset++;
|
||||
if (words[i][offset] == '<') {
|
||||
while (words[i][offset] != '>') {
|
||||
offset++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
offset = Math.max(offset-4, 0);
|
||||
}
|
||||
}
|
||||
document.getElementsByClassName('word')[0].innerHTML = part;
|
||||
},speed);
|
||||
};
|
||||
|
||||
window.onload = function () {
|
||||
wordflick();
|
||||
};
|
||||
</script>
|
||||
|
||||
|
||||
<header><div class="logo_container">
|
||||
<div class="word"></div>
|
||||
<h1 class="logo">Kraken</h1>
|
||||
</div></header>
|
||||
<i>FOSS Fexprs: <a title="Kraken on GitHub" href="https://github.com/limvot/kraken">https://github.com/limvot/kraken</a></i>
|
||||
<!--<button onclick="toggleTheme()" style="float: right;">Swap Theme</button>-->
|
||||
<br>
|
||||
<h3>Concept:</h3>
|
||||
<ul>
|
||||
<li> Minimal, purely functional Kernel/Scheme as 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> Partial evaluation (or now, maybe tracing JIT compilation) to make fexprs fast (my PhD research! First paper on <a href="https://arxiv.org/abs/2303.12254">arXiv</a>)
|
||||
<li> Implement Type Systems as Macros (but using Fexprs 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 fexprs" to add types and create a statically-typed language on top (with Algebraic Effects using the underlying delimited continuations, etc)
|
||||
</ul>
|
||||
<h3> About:</h3>
|
||||
<p>This is my 4th run at this Lisp concept, with tracing JIT compilation to make fexprs fast forming the core of my current PhD research. <a href="https://miloignis.room409.xyz/">(tiny personal PhD website here)</a></p>
|
||||
<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>
|
||||
<h3>Next Steps</h3>
|
||||
<ul>
|
||||
<li> Implement persistent functional data structures
|
||||
<ul>
|
||||
<li> ✔ RB-Tree
|
||||
<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> Sketch out Kraken language on top of core Lisp, includes basic Hindley-Milner type system
|
||||
<li> Re-self-host using functional approach in above Kraken language
|
||||
</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]);
|
||||
}
|
||||
function toggleTheme() {
|
||||
let theme = document.getElementById('theme')
|
||||
if (theme.getAttribute("href") == "recursive.css") {
|
||||
theme.setAttribute("href", "slick.css");
|
||||
} else {
|
||||
theme.setAttribute("href", "recursive.css");
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<script type="text/javascript" src="k_prime.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -27,7 +27,8 @@ body {
|
||||
font: 1.2em/1.62 'Recursive', sans-serif;
|
||||
}
|
||||
//body, .remark-slide-content { background-color: #eff3f5; }
|
||||
body, .remark-slide-content { background-color: #f5f3ef; }
|
||||
//body, .remark-slide-content { background-color: #f5f3ef; }
|
||||
body, .remark-slide-content { background-color: #f0f6f0; color: #222323; }
|
||||
h1, h2, h3, h4 {
|
||||
line-height:0.4;
|
||||
--rec-wght: 900;
|
||||
@@ -38,9 +39,53 @@ h1, h2, h3, h4 {
|
||||
letter-spacing: -0.015em;
|
||||
font-size: 4em;
|
||||
}
|
||||
h1 {
|
||||
.top_spacer {
|
||||
position: static;
|
||||
height: 20vh;
|
||||
}
|
||||
.word {
|
||||
/*height: 20vh;*/
|
||||
/*font-size: 7cqw;*/
|
||||
font-size: 6cqw;
|
||||
font-family: 'Recursive', monospace;
|
||||
--rec-mono: 1;
|
||||
letter-spacing: normal;
|
||||
tab-size: 4;
|
||||
|
||||
position: absolute;
|
||||
top: 15%;
|
||||
bottom: auto;
|
||||
right: auto;
|
||||
display: block;
|
||||
}
|
||||
.logo {
|
||||
text-decoration: underline;
|
||||
text-decoration-thickness: 0.4rem;
|
||||
|
||||
/*font-size: 13em;*/
|
||||
/*font-size: 15vw;*/
|
||||
font-size: 28cqw;
|
||||
/*line-height:0.4;*/
|
||||
--rec-wght: 900;
|
||||
--rec-slnt: 0;
|
||||
--rec-casl: 0.0;
|
||||
--rec-crsv: 1;
|
||||
--rec-mono: 0;
|
||||
/*letter-spacing: -0.015em;*/
|
||||
letter-spacing: 0em;
|
||||
position: absolute;
|
||||
top: auto;
|
||||
bottom: 2%;
|
||||
right: auto;
|
||||
display: block;
|
||||
/*overflow: hidden;*/
|
||||
}
|
||||
.logo_container {
|
||||
position: static;
|
||||
/*height: 100vh;*/
|
||||
height: 100vh;
|
||||
container-type: inline-size;
|
||||
/*max-width: initial;*/
|
||||
}
|
||||
h2 { font-size: 3em; }
|
||||
h3 { font-size: 1.5em; }
|
||||
|
||||
Reference in New Issue
Block a user