Initial ID & func call stats!
This commit is contained in:
@@ -1,22 +1,23 @@
|
||||
use std::str::FromStr;
|
||||
use std::rc::Rc;
|
||||
use std::cell::RefCell;
|
||||
use sl::Form;
|
||||
|
||||
grammar;
|
||||
|
||||
pub Term: Rc<Form> = {
|
||||
NUM => Rc::new(Form::Int(i32::from_str(<>).unwrap())),
|
||||
SYM => Rc::new(Form::Symbol(<>.to_owned(),None)),
|
||||
SYM => Rc::new(Form::Symbol(<>.to_owned(),RefCell::new(None))),
|
||||
"(" <ListInside?> ")" => <>.unwrap_or(Rc::new(Form::Nil)),
|
||||
"'" <Term> => Rc::new(Form::Pair(Rc::new(Form::Symbol("quote".to_owned(),None)), Rc::new(Form::Pair(<>, Rc::new(Form::Nil),None)),None)),
|
||||
"'" <Term> => Rc::new(Form::Pair(Rc::new(Form::Symbol("quote".to_owned(),RefCell::new(None))), Rc::new(Form::Pair(<>, Rc::new(Form::Nil),RefCell::new(None))),RefCell::new(None))),
|
||||
"!" <h: Term> <t: Term> => {
|
||||
h.append(t).unwrap()
|
||||
},
|
||||
};
|
||||
ListInside: Rc<Form> = {
|
||||
<Term> => Rc::new(Form::Pair(<>, Rc::new(Form::Nil),None)),
|
||||
<h: Term> <t: ListInside> => Rc::new(Form::Pair(h, t,None)),
|
||||
<a: Term> "." <d: Term> => Rc::new(Form::Pair(a, d,None)),
|
||||
<Term> => Rc::new(Form::Pair(<>, Rc::new(Form::Nil),RefCell::new(None))),
|
||||
<h: Term> <t: ListInside> => Rc::new(Form::Pair(h, t,RefCell::new(None))),
|
||||
<a: Term> "." <d: Term> => Rc::new(Form::Pair(a, d,RefCell::new(None))),
|
||||
}
|
||||
match {
|
||||
"(",
|
||||
|
||||
105
sl/src/lib.rs
105
sl/src/lib.rs
@@ -5,7 +5,25 @@ use std::cell::RefCell;
|
||||
|
||||
use anyhow::{anyhow,bail,Result};
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
|
||||
|
||||
// This first Simple Lisp really is
|
||||
//
|
||||
// No fexprs, no mutation, no continuations, no macros, no strings.
|
||||
// Int/Bool/Nil/Pair/Symbol/Closure/Prim.
|
||||
//
|
||||
// Figuring out GC between a JIT and Rust will be tricky.
|
||||
// Can start with a like tracing-JIT-into-bytecode
|
||||
// Replcing Env with pairs or somesuch would make JIT interop easier I think, because we wouldn't
|
||||
// have to deal with refcell, but then we would again for mutation.
|
||||
// Maybe doing all allocation on the Rust side with #[no_mangle] functions would make things easier
|
||||
// mmmm no let's make our own Box, Rc, maybe Arc
|
||||
// rustonomicon
|
||||
|
||||
// What if we're cute and use the ID
|
||||
// like we will eventually use value tagging
|
||||
// like, use the same encoding
|
||||
// interned symbols and all
|
||||
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Clone, Copy)]
|
||||
pub struct ID {
|
||||
id: i64
|
||||
}
|
||||
@@ -15,9 +33,9 @@ pub enum Form {
|
||||
Nil,
|
||||
Int(i32),
|
||||
Bool(bool),
|
||||
Symbol(String, Option<RefCell<ID>>),
|
||||
Pair(Rc<Form>, Rc<Form>, Option<RefCell<ID>>),
|
||||
Closure(Vec<String>, Rc<RefCell<Env>>, Rc<Form>, Option<RefCell<ID>>),
|
||||
Symbol(String, RefCell<Option<ID>>),
|
||||
Pair(Rc<Form>, Rc<Form>, RefCell<Option<ID>>),
|
||||
Closure(Vec<String>, Rc<RefCell<Env>>, Rc<Form>, RefCell<Option<ID>>),
|
||||
Prim(Prim),
|
||||
}
|
||||
|
||||
@@ -47,7 +65,7 @@ impl Form {
|
||||
}
|
||||
}
|
||||
fn new_pair(car: Rc<Form>, cdr: Rc<Form>) -> Rc<Form> {
|
||||
Rc::new(Form::Pair(car, cdr, None))
|
||||
Rc::new(Form::Pair(car, cdr, RefCell::new(None)))
|
||||
}
|
||||
fn new_nil() -> Rc<Form> {
|
||||
Rc::new(Form::Nil)
|
||||
@@ -59,7 +77,7 @@ impl 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, None))
|
||||
Rc::new(Form::Closure(params, env, body, RefCell::new(None)))
|
||||
}
|
||||
fn truthy(&self) -> bool {
|
||||
match self {
|
||||
@@ -118,8 +136,8 @@ impl Form {
|
||||
}
|
||||
pub fn append(&self, x: Rc<Form>) -> Result<Rc<Form>> {
|
||||
match self {
|
||||
Form::Pair(car, cdr, _id) => cdr.append(x).map(|x| Rc::new(Form::Pair(Rc::clone(car), x, None))),
|
||||
Form::Nil => Ok(Rc::new(Form::Pair(x, Rc::new(Form::Nil), None))),
|
||||
Form::Pair(car, cdr, _id) => cdr.append(x).map(|x| Rc::new(Form::Pair(Rc::clone(car), x, RefCell::new(None)))),
|
||||
Form::Nil => Ok(Rc::new(Form::Pair(x, Rc::new(Form::Nil), RefCell::new(None)))),
|
||||
_ => Err(anyhow!("append to not a pair")),
|
||||
}
|
||||
}
|
||||
@@ -164,10 +182,50 @@ impl Env {
|
||||
}
|
||||
}
|
||||
pub fn define(&mut self, s: String, v: Rc<Form>) {
|
||||
// no mutation, shadowing in inner scope ok
|
||||
assert!(!self.m.contains_key(&s));
|
||||
self.m.insert(s, v);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Stats {
|
||||
id_counter: i64,
|
||||
func_calls: BTreeMap<ID, i64>,
|
||||
}
|
||||
impl Stats {
|
||||
fn new() -> Stats {
|
||||
Stats {
|
||||
id_counter: 0,
|
||||
func_calls: BTreeMap::new()
|
||||
}
|
||||
}
|
||||
fn alloc_id(&mut self) -> ID {
|
||||
self.id_counter += 1;
|
||||
ID { id: self.id_counter }
|
||||
}
|
||||
fn count_call(&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();
|
||||
let entry = self.func_calls.entry(id).or_insert(0);
|
||||
*entry += 1;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eval(f: Rc<Form>) -> Result<Rc<Form>> {
|
||||
let e = Env::root_env();
|
||||
let mut stats = Stats::new();
|
||||
let to_ret = tree_walker_eval(f, e, &mut stats)?;
|
||||
println!("Stats were {stats:?}");
|
||||
Ok(to_ret)
|
||||
}
|
||||
|
||||
// add functions
|
||||
// variables
|
||||
// optimized as a function based off side table of id keyed -> opt
|
||||
@@ -175,17 +233,17 @@ impl Env {
|
||||
// Symbol ID's could actually be used for environment lookups
|
||||
// this is just interning
|
||||
|
||||
pub fn tree_walker_eval(f: Rc<Form>, e: Rc<RefCell<Env>>) -> Result<Rc<Form>> {
|
||||
fn tree_walker_eval(f: Rc<Form>, e: Rc<RefCell<Env>>, stats: &mut Stats) -> Result<Rc<Form>> {
|
||||
println!("tree_walker_eval({f})");
|
||||
Ok(match &*f {
|
||||
Form::Symbol(s, _id) => e.borrow().lookup(s)?,
|
||||
Form::Pair(car, cdr, _id) => {
|
||||
match &**car {
|
||||
Form::Symbol(s, _id) if s == "if" => {
|
||||
if tree_walker_eval(cdr.car()?, Rc::clone(&e))?.truthy() {
|
||||
tree_walker_eval(cdr.cdr()?.car()?, e)?
|
||||
if tree_walker_eval(cdr.car()?, Rc::clone(&e), stats)?.truthy() {
|
||||
tree_walker_eval(cdr.cdr()?.car()?, e, stats)?
|
||||
} else {
|
||||
tree_walker_eval(cdr.cdr()?.cdr()?.car()?, e)?
|
||||
tree_walker_eval(cdr.cdr()?.cdr()?.car()?, e, stats)?
|
||||
}
|
||||
|
||||
}
|
||||
@@ -194,19 +252,19 @@ pub fn tree_walker_eval(f: Rc<Form>, e: Rc<RefCell<Env>>) -> Result<Rc<Form>> {
|
||||
let mut traverse = Rc::clone(cdr);
|
||||
while let Ok((ncar, ncdr)) = traverse.pair() {
|
||||
traverse = ncdr;
|
||||
last_result = tree_walker_eval(ncar, Rc::clone(&e))?;
|
||||
last_result = tree_walker_eval(ncar, Rc::clone(&e), stats)?;
|
||||
}
|
||||
last_result
|
||||
|
||||
}
|
||||
Form::Symbol(s, _id) if s == "debug" => {
|
||||
println!("debug: {}", tree_walker_eval(cdr.car()?, e)?);
|
||||
println!("debug: {}", tree_walker_eval(cdr.car()?, e, stats)?);
|
||||
Form::new_nil()
|
||||
}
|
||||
// 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" => {
|
||||
let v = tree_walker_eval(cdr.cdr()?.car()?, Rc::clone(&e))?;
|
||||
let v = tree_walker_eval(cdr.cdr()?.car()?, Rc::clone(&e), stats)?;
|
||||
e.borrow_mut().define(cdr.car()?.sym()?.to_string(), v);
|
||||
Form::new_nil()
|
||||
}
|
||||
@@ -225,13 +283,14 @@ pub fn tree_walker_eval(f: Rc<Form>, e: Rc<RefCell<Env>>) -> Result<Rc<Form>> {
|
||||
Form::new_closure(params_vec, Rc::clone(&e), body)
|
||||
}
|
||||
_ => {
|
||||
let comb = tree_walker_eval(Rc::clone(car), Rc::clone(&e))?;
|
||||
let comb = tree_walker_eval(Rc::clone(car), Rc::clone(&e), stats)?;
|
||||
match &*comb {
|
||||
Form::Closure(ps, ie, b, id) => {
|
||||
stats.count_call(id);
|
||||
let mut arguments_vec = vec![];
|
||||
let mut arguments = Rc::clone(cdr);
|
||||
while let Ok((ncar, ncdr)) = arguments.pair() {
|
||||
arguments_vec.push(tree_walker_eval(ncar, Rc::clone(&e))?);
|
||||
arguments_vec.push(tree_walker_eval(ncar, Rc::clone(&e), stats)?);
|
||||
arguments = ncdr;
|
||||
}
|
||||
if ps.len() != arguments_vec.len() {
|
||||
@@ -241,15 +300,15 @@ pub fn tree_walker_eval(f: Rc<Form>, e: Rc<RefCell<Env>>) -> Result<Rc<Form>> {
|
||||
for (name, value) in ps.iter().zip(arguments_vec.into_iter()) {
|
||||
new_env.borrow_mut().define(name.to_string(), value);
|
||||
}
|
||||
tree_walker_eval(Rc::clone(b), new_env)?
|
||||
tree_walker_eval(Rc::clone(b), new_env, stats)?
|
||||
},
|
||||
Form::Prim(p) => {
|
||||
let a = tree_walker_eval(cdr.car()?, Rc::clone(&e))?;
|
||||
let a = tree_walker_eval(cdr.car()?, Rc::clone(&e), stats)?;
|
||||
match comb.prim().unwrap() {
|
||||
Prim::Car => a.car()?,
|
||||
Prim::Cdr => a.cdr()?,
|
||||
_ => {
|
||||
let b = tree_walker_eval(cdr.cdr()?.car()?, Rc::clone(&e))?;
|
||||
let b = tree_walker_eval(cdr.cdr()?.car()?, Rc::clone(&e), stats)?;
|
||||
match comb.prim().unwrap() {
|
||||
Prim::Add => Form::new_int(a.int()? + b.int()?),
|
||||
Prim::Sub => Form::new_int(a.int()? - b.int()?),
|
||||
@@ -275,13 +334,13 @@ pub fn tree_walker_eval(f: Rc<Form>, e: Rc<RefCell<Env>>) -> Result<Rc<Form>> {
|
||||
}
|
||||
|
||||
// todo, strings not symbols?
|
||||
impl From<String> for Form { fn from(item: String) -> Self { Form::Symbol(item, None) } }
|
||||
impl From<&str> for Form { fn from(item: &str) -> Self { Form::Symbol(item.to_owned(), None) } }
|
||||
impl From<String> for Form { fn from(item: String) -> Self { Form::Symbol(item, RefCell::new(None)) } }
|
||||
impl From<&str> for Form { fn from(item: &str) -> Self { Form::Symbol(item.to_owned(), RefCell::new(None)) } }
|
||||
impl From<i32> for Form { fn from(item: i32) -> Self { Form::Int(item) } }
|
||||
impl From<bool> for Form { fn from(item: bool) -> Self { Form::Bool(item) } }
|
||||
impl<A: Into<Form>, B: Into<Form>> From<(A, B)> for Form {
|
||||
fn from(item: (A, B)) -> Self {
|
||||
Form::Pair(Rc::new(item.0.into()), Rc::new(item.1.into()), None)
|
||||
Form::Pair(Rc::new(item.0.into()), Rc::new(item.1.into()), RefCell::new(None))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,32 +4,31 @@ lalrpop_mod!(pub grammar);
|
||||
use std::rc::Rc;
|
||||
use anyhow::Result;
|
||||
|
||||
use sl::{tree_walker_eval, Env};
|
||||
use sl::eval;
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let input = "
|
||||
(begin
|
||||
(debug 1)
|
||||
(debug (= 1 2))
|
||||
(debug (+ 2 3))
|
||||
(define a (+ 1 (* 3 4)))
|
||||
;(debug (= 1 2))
|
||||
;(debug (+ 2 3))
|
||||
;(define a (+ 1 (* 3 4)))
|
||||
(define fact (lambda (n) (if (= n 1) 1 (* n (fact (- n 1))))))
|
||||
(debug 'gonna_fact_it)
|
||||
(debug fact)
|
||||
(debug (fact 5))
|
||||
(debug a)
|
||||
(define b (cons 1 (cons 2 (cons 3 nil))))
|
||||
(debug b)
|
||||
(debug (car b))
|
||||
(debug (cdr b))
|
||||
;(debug a)
|
||||
;(define b (cons 1 (cons 2 (cons 3 nil))))
|
||||
;(debug b)
|
||||
;(debug (car b))
|
||||
;(debug (cdr b))
|
||||
(if (= 1 2) (+ 2 3) (* 2 2))
|
||||
)
|
||||
";
|
||||
let parsed_input = Rc::new(grammar::TermParser::new().parse(input)?);
|
||||
//println!("Hello, world: {parsed_input:?}");
|
||||
println!("Hello, world: {parsed_input}");
|
||||
let e = Env::root_env();
|
||||
let tree_walker_evaled = tree_walker_eval(Rc::clone(&parsed_input), e)?;
|
||||
println!("tree walker evaled: {tree_walker_evaled}");
|
||||
let evaled = eval(Rc::clone(&parsed_input))?;
|
||||
println!("evaled: {evaled}");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user