Compare commits

..

No commits in common. "61b1b7bf90a62ee5beabf5c71c6416751c61d52c" and "c497b90a16bb248c376378ac9d67248ff1255ff8" have entirely different histories.

5 changed files with 76 additions and 225 deletions

View File

@ -53,7 +53,16 @@ pub fn store(b: &Value, val: &Value) -> Value {
// name, patterns, AND docstring // name, patterns, AND docstring
pub fn doc(f: &Value) -> Value { pub fn doc(f: &Value) -> Value {
match f { match f {
Value::Fn(f) => f.as_ref().doc(), Value::Fn(f) => {
let lfn = f.as_ref().get().unwrap();
let name = lfn.name;
let doc = lfn.doc;
if let Some(docstr) = doc {
Value::String(Rc::new(format!("{name}: {docstr}")))
} else {
Value::String(Rc::new(format!("{name}: no documentation found")))
}
}
_ => Value::Interned("no documentation found"), _ => Value::Interned("no documentation found"),
} }
} }

View File

@ -5,7 +5,7 @@ use crate::value::*;
use chumsky::prelude::SimpleSpan; use chumsky::prelude::SimpleSpan;
use num_derive::{FromPrimitive, ToPrimitive}; use num_derive::{FromPrimitive, ToPrimitive};
use num_traits::FromPrimitive; use num_traits::FromPrimitive;
use std::cell::RefCell; use std::cell::OnceCell;
use std::collections::HashMap; use std::collections::HashMap;
use std::rc::Rc; use std::rc::Rc;
@ -76,9 +76,6 @@ pub enum Op {
At, At,
Not, Not,
Print,
SetUpvalue,
GetUpvalue,
// Inc, // Inc,
// Dec, // Dec,
// Gt, // Gt,
@ -172,7 +169,6 @@ impl std::fmt::Display for Op {
EmptyString => "empty_string", EmptyString => "empty_string",
ConcatStrings => "concat_strings", ConcatStrings => "concat_strings",
Stringify => "stringify", Stringify => "stringify",
Print => "print",
Eq => "eq", Eq => "eq",
Add => "add", Add => "add",
@ -189,9 +185,6 @@ impl std::fmt::Display for Op {
Call => "call", Call => "call",
Return => "return", Return => "return",
SetUpvalue => "set_upvalue",
GetUpvalue => "get_upvalue",
}; };
write!(f, "{rep}") write!(f, "{rep}")
} }
@ -204,12 +197,6 @@ pub struct Binding {
stack_pos: usize, stack_pos: usize,
} }
#[derive(Clone, Debug, PartialEq)]
pub struct Upvalue {
name: &'static str,
stack_pos: usize,
}
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub struct Chunk { pub struct Chunk {
pub constants: Vec<Value>, pub constants: Vec<Value>,
@ -227,7 +214,7 @@ impl Chunk {
| PanicIfNoMatch | ResetMatch | GetKey | PanicNoWhen | PanicNoMatch | TypeOf | PanicIfNoMatch | ResetMatch | GetKey | PanicNoWhen | PanicNoMatch | TypeOf
| Duplicate | Decrement | Truncate | Noop | LoadTuple | LoadList | Eq | Add | Sub | Duplicate | Decrement | Truncate | Noop | LoadTuple | LoadList | Eq | Add | Sub
| Mult | Div | Unbox | BoxStore | Assert | Get | At | Not | Panic | EmptyString | Mult | Div | Unbox | BoxStore | Assert | Get | At | Not | Panic | EmptyString
| ConcatStrings | Stringify | MatchType | Return | Match | Print => { | ConcatStrings | Stringify | MatchType | Return | Match => {
println!("{i:04}: {op}") println!("{i:04}: {op}")
} }
Constant | MatchConstant => { Constant | MatchConstant => {
@ -238,8 +225,7 @@ impl Chunk {
} }
PushBinding | MatchTuple | MatchList | MatchDict | LoadDictValue | PushTuple PushBinding | MatchTuple | MatchList | MatchDict | LoadDictValue | PushTuple
| PushDict | PushList | PushBox | Jump | JumpIfFalse | JumpIfTrue | JumpIfNoMatch | PushDict | PushList | PushBox | Jump | JumpIfFalse | JumpIfTrue | JumpIfNoMatch
| JumpIfMatch | JumpBack | JumpIfZero | MatchDepth | PopN | StoreAt | Call | JumpIfMatch | JumpBack | JumpIfZero | MatchDepth | PopN | StoreAt | Call => {
| SetUpvalue | GetUpvalue => {
let next = self.bytecode[*i + 1]; let next = self.bytecode[*i + 1];
println!("{i:04}: {:16} {next:04}", op.to_string()); println!("{i:04}: {:16} {next:04}", op.to_string());
*i += 1; *i += 1;
@ -260,9 +246,9 @@ impl Chunk {
// self.kw_index_from(kw).map(Value::Keyword) // self.kw_index_from(kw).map(Value::Keyword)
// } // }
// pub fn kw_index_from(&self, kw: &str) -> Option<usize> { pub fn kw_index_from(&self, kw: &str) -> Option<usize> {
// self.keywords.iter().position(|s| *s == kw) self.keywords.iter().position(|s| *s == kw)
// } }
} }
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
@ -291,14 +277,13 @@ fn get_builtin(name: &str, arity: usize) -> Option<Op> {
("get", 2) => Some(Op::Get), ("get", 2) => Some(Op::Get),
("at", 2) => Some(Op::At), ("at", 2) => Some(Op::At),
("not", 1) => Some(Op::Not), ("not", 1) => Some(Op::Not),
("print!", 1) => Some(Op::Print),
_ => None, _ => None,
} }
} }
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct Compiler<'a> { pub struct Compiler {
pub chunk: Chunk, pub chunk: Chunk,
pub bindings: Vec<Binding>, pub bindings: Vec<Binding>,
pub scope_depth: isize, pub scope_depth: isize,
@ -310,8 +295,6 @@ pub struct Compiler<'a> {
pub span: SimpleSpan, pub span: SimpleSpan,
pub src: &'static str, pub src: &'static str,
pub name: &'static str, pub name: &'static str,
pub enclosing: Option<&'a Compiler<'a>>,
pub upvalues: Vec<Upvalue>,
loop_info: Vec<LoopInfo>, loop_info: Vec<LoopInfo>,
} }
@ -320,18 +303,13 @@ fn is_binding(expr: &Spanned<Ast>) -> bool {
use Ast::*; use Ast::*;
match ast { match ast {
Let(..) | LBox(..) => true, Let(..) | LBox(..) => true,
// Fn(name, ..) => !name.is_empty(), Fn(name, ..) => !name.is_empty(),
_ => false, _ => false,
} }
} }
impl<'a> Compiler<'a> { impl Compiler {
pub fn new( pub fn new(ast: &'static Spanned<Ast>, name: &'static str, src: &'static str) -> Compiler {
ast: &'static Spanned<Ast>,
name: &'static str,
src: &'static str,
enclosing: Option<&'a Compiler>,
) -> Compiler<'a> {
let chunk = Chunk { let chunk = Chunk {
constants: vec![], constants: vec![],
bytecode: vec![], bytecode: vec![],
@ -351,8 +329,6 @@ impl<'a> Compiler<'a> {
ast: &ast.0, ast: &ast.0,
span: ast.1, span: ast.1,
loop_info: vec![], loop_info: vec![],
enclosing,
upvalues: vec![],
src, src,
name, name,
} }
@ -455,40 +431,6 @@ impl<'a> Compiler<'a> {
self.bindings.push(binding); self.bindings.push(binding);
} }
fn resolve_local(&self, name: &'static str) -> Option<usize> {
for binding in self.bindings.iter() {
if binding.name == name {
return Some(binding.stack_pos);
}
}
None
}
fn resolve_upvalue(&self, name: &'static str) -> Option<usize> {
self.upvalues.iter().position(|uv| uv.name == name)
}
fn get_upvalue(&self, name: &'static str) -> Upvalue {
let local = self.bindings.iter().find(|b| b.name == name);
match local {
Some(binding) => Upvalue {
name,
stack_pos: binding.stack_pos,
},
None => self.enclosing.unwrap().get_upvalue(name),
}
}
fn resolve_binding(&self, name: &'static str) -> usize {
if let Some(pos) = self.resolve_local(name) {
return pos;
}
match self.enclosing {
Some(compiler) => compiler.resolve_binding(name),
None => unreachable!(),
}
}
fn pop(&mut self) { fn pop(&mut self) {
self.emit_op(Op::Pop); self.emit_op(Op::Pop);
self.stack_depth -= 1; self.stack_depth -= 1;
@ -612,30 +554,17 @@ impl<'a> Compiler<'a> {
self.emit_op(Op::Match); self.emit_op(Op::Match);
self.bind(name); self.bind(name);
} }
Word(name) => match self.resolve_local(name) { Word(name) => {
Some(position) => { self.emit_op(Op::PushBinding);
self.emit_op(Op::PushBinding); self.stack_depth += 1;
self.emit_byte(position); let biter = self.bindings.iter().rev();
self.stack_depth += 1; for binding in biter {
if binding.name == *name {
self.emit_byte(binding.stack_pos);
break;
}
} }
None => match self.resolve_upvalue(name) { }
Some(position) => {
println!("resolved upvalue: {name}");
self.emit_op(Op::GetUpvalue);
self.emit_byte(position);
self.stack_depth += 1;
}
None => {
println!("setting upvalue: {name}");
let upvalue = self.get_upvalue(name);
self.emit_op(Op::GetUpvalue);
self.emit_byte(upvalue.stack_pos);
self.upvalues.push(upvalue);
dbg!(&self.upvalues);
self.stack_depth += 1;
}
},
},
PlaceholderPattern => { PlaceholderPattern => {
self.emit_op(Op::Match); self.emit_op(Op::Match);
} }
@ -930,6 +859,7 @@ impl<'a> Compiler<'a> {
}, },
_ => unreachable!(), _ => unreachable!(),
} }
// TODO: implement longer synthetic expressions
for (term, _) in rest { for (term, _) in rest {
match term { match term {
Keyword(str) => { Keyword(str) => {
@ -1050,7 +980,6 @@ impl<'a> Compiler<'a> {
} }
MatchClause(..) => unreachable!(), MatchClause(..) => unreachable!(),
Fn(name, body, doc) => { Fn(name, body, doc) => {
let name = if name.is_empty() { "anonymous" } else { name };
// first, declare the function // first, declare the function
// TODO: or, check if the function has already been declared! // TODO: or, check if the function has already been declared!
let FnBody(fn_body) = &body.as_ref().0 else { let FnBody(fn_body) = &body.as_ref().0 else {
@ -1059,8 +988,6 @@ impl<'a> Compiler<'a> {
let mut compilers: HashMap<usize, Compiler> = HashMap::new(); let mut compilers: HashMap<usize, Compiler> = HashMap::new();
let mut upvalues = vec![];
for clause in fn_body { for clause in fn_body {
let MatchClause(pattern, _, clause_body) = &clause.0 else { let MatchClause(pattern, _, clause_body) = &clause.0 else {
unreachable!() unreachable!()
@ -1074,8 +1001,8 @@ impl<'a> Compiler<'a> {
let compiler = match compilers.get_mut(&arity) { let compiler = match compilers.get_mut(&arity) {
Some(compiler) => compiler, Some(compiler) => compiler,
None => { None => {
let mut compiler = let mut compiler = Compiler::new(clause, self.name, self.src);
Compiler::new(clause, self.name, self.src, Some(self)); // compiler.emit_op(Op::Load);
compiler.emit_op(Op::ResetMatch); compiler.emit_op(Op::ResetMatch);
compilers.insert(arity, compiler); compilers.insert(arity, compiler);
compilers.get_mut(&arity).unwrap() compilers.get_mut(&arity).unwrap()
@ -1086,8 +1013,6 @@ impl<'a> Compiler<'a> {
compiler.scope_depth += 1; compiler.scope_depth += 1;
compiler.match_depth = arity; compiler.match_depth = arity;
std::mem::swap(&mut upvalues, &mut compiler.upvalues);
let mut tup_jump_idxes = vec![]; let mut tup_jump_idxes = vec![];
for member in pattern { for member in pattern {
compiler.match_depth -= 1; compiler.match_depth -= 1;
@ -1131,8 +1056,6 @@ impl<'a> Compiler<'a> {
compiler.emit_op(Op::Return); compiler.emit_op(Op::Return);
compiler.chunk.bytecode[jnm_idx] = compiler.len() as u8 - jnm_idx as u8 - 1; compiler.chunk.bytecode[jnm_idx] = compiler.len() as u8 - jnm_idx as u8 - 1;
compiler.scope_depth -= 1; compiler.scope_depth -= 1;
std::mem::swap(&mut compiler.upvalues, &mut upvalues);
} }
let mut the_chunks = vec![]; let mut the_chunks = vec![];
@ -1147,26 +1070,25 @@ impl<'a> Compiler<'a> {
the_chunks.push((arity as u8, chunk)); the_chunks.push((arity as u8, chunk));
} }
let lfn = crate::value::LFn::Defined { let lfn = crate::value::LFn {
name, name,
doc: *doc, doc: *doc,
chunks: the_chunks, chunks: the_chunks,
closed: RefCell::new(vec![]), closed: vec![],
}; };
// TODO: check if the function is already declared, and pull out the relevant OnceCell if need be let cell = OnceCell::new();
let init_val = Value::Fn(Rc::new(lfn)); let _ = cell.set(lfn);
let init_val = Value::Fn(Rc::new(cell));
self.emit_constant(init_val); self.emit_constant(init_val);
self.bind(name); self.bind(name);
// TODO: close over everything accessed in the function // TODO: close over everything accessed in the function
for upvalue in upvalues {
self.emit_op(Op::SetUpvalue); // TODO: pull the function off the stack, and set the OnceCell.
self.emit_byte(upvalue.stack_pos);
}
} }
FnDeclaration(name) => { FnDeclaration(name) => {
let lfn = Value::Fn(Rc::new(LFn::Declared { name })); let lfn = Value::Fn(Rc::new(OnceCell::new()));
self.emit_constant(lfn); self.emit_constant(lfn);
self.bind(name); self.bind(name);
} }

View File

@ -48,7 +48,7 @@ pub fn run(src: &'static str) {
// in any event, the AST should live forever // in any event, the AST should live forever
let parsed: &'static Spanned<Ast> = Box::leak(Box::new(parse_result.unwrap())); let parsed: &'static Spanned<Ast> = Box::leak(Box::new(parse_result.unwrap()));
let mut compiler = Compiler::new(parsed, "test", src, None); let mut compiler = Compiler::new(parsed, "test", src);
compiler.compile(); compiler.compile();
if DEBUG_COMPILE { if DEBUG_COMPILE {
println!("=== source code ==="); println!("=== source code ===");
@ -76,12 +76,9 @@ pub fn run(src: &'static str) {
pub fn main() { pub fn main() {
env::set_var("RUST_BACKTRACE", "1"); env::set_var("RUST_BACKTRACE", "1");
let src = " let src = "
let foo = { fn foo () -> fn () -> fn () -> #{:foo :bar}
let bar = :bar
fn baz () -> bar
}
foo () foo () () () :foo
"; ";
run(src); run(src);
} }

View File

@ -3,84 +3,24 @@ use crate::compiler::Chunk;
use crate::parser::Ast; use crate::parser::Ast;
use crate::spans::Spanned; use crate::spans::Spanned;
use imbl::{HashMap, Vector}; use imbl::{HashMap, Vector};
use std::cell::RefCell; use std::cell::{OnceCell, RefCell};
use std::rc::Rc; use std::rc::Rc;
// #[derive(Clone, Debug, PartialEq)]
// pub struct Defined {
// pub name: &'static str,
// pub doc: Option<&'static str>,
// // pub enclosing: Vec<(usize, Value)>,
// // pub has_run: bool,
// // pub input: &'static str,
// // pub src: &'static str,
// pub chunks: Vec<(u8, Chunk)>,
// pub closed: Vec<Value>,
// }
// impl Defined {
// pub fn close(&mut self, val: Value) {
// self.closed.push(val);
// }
// }
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub enum LFn { pub struct LFn {
Declared { pub name: &'static str,
name: &'static str, pub doc: Option<&'static str>,
}, // pub enclosing: Vec<(usize, Value)>,
Defined { // pub has_run: bool,
name: &'static str, // pub input: &'static str,
doc: Option<&'static str>, // pub src: &'static str,
chunks: Vec<(u8, Chunk)>, pub chunks: Vec<(u8, Chunk)>,
closed: RefCell<Vec<Value>>, pub closed: Vec<Value>,
},
} }
impl LFn { impl LFn {
pub fn close(&self, value: Value) { pub fn close(&mut self, val: Value) {
match self { self.closed.push(val);
LFn::Declared { .. } => unreachable!(),
LFn::Defined { closed, .. } => {
closed.borrow_mut().push(value);
}
}
}
pub fn doc(&self) -> Value {
match self {
LFn::Declared { name } => {
Value::String(Rc::new(format!("fn {name}: undefined function")))
}
LFn::Defined {
name,
doc: Some(doc),
..
} => Value::String(Rc::new(format!("fn {name}\n{doc}"))),
LFn::Defined { name, .. } => {
Value::String(Rc::new(format!("fn {name}: no documentation")))
}
}
}
pub fn name(&self) -> &'static str {
match self {
LFn::Declared { name } | LFn::Defined { name, .. } => name,
}
}
pub fn chunk(&self, arity: u8) -> &Chunk {
match self {
LFn::Declared { .. } => unreachable!(),
LFn::Defined { chunks, .. } => &chunks.iter().find(|(a, _)| *a == arity).unwrap().1,
}
}
pub fn upvalue(&self, idx: u8) -> Value {
match self {
LFn::Declared { .. } => unreachable!(),
LFn::Defined { closed, .. } => closed.borrow()[idx as usize].clone(),
}
} }
} }
@ -98,7 +38,7 @@ pub enum Value {
List(Box<Vector<Value>>), List(Box<Vector<Value>>),
Dict(Box<HashMap<&'static str, Value>>), Dict(Box<HashMap<&'static str, Value>>),
Box(Rc<RefCell<Value>>), Box(Rc<RefCell<Value>>),
Fn(Rc<LFn>), Fn(Rc<OnceCell<LFn>>),
BaseFn(BaseFn), BaseFn(BaseFn),
} }
@ -163,7 +103,7 @@ impl std::fmt::Display for Value {
.join(", ") .join(", ")
), ),
Box(value) => write!(f, "box {}", value.as_ref().borrow()), Box(value) => write!(f, "box {}", value.as_ref().borrow()),
Fn(lfn) => write!(f, "fn {}", lfn.name()), Fn(lfn) => write!(f, "fn {}", lfn.get().unwrap().name),
BaseFn(_) => write!(f, "base fn"), BaseFn(_) => write!(f, "base fn"),
} }
} }
@ -204,7 +144,7 @@ impl Value {
format!("#{{{members}}}") format!("#{{{members}}}")
} }
Box(x) => format!("box {{ {} }}", x.as_ref().borrow().show()), Box(x) => format!("box {{ {} }}", x.as_ref().borrow().show()),
Fn(lfn) => format!("fn {}", lfn.name()), Fn(lfn) => format!("fn {}", lfn.get().unwrap().name),
BaseFn(_) => "base fn".to_string(), BaseFn(_) => "base fn".to_string(),
Nothing => unreachable!(), Nothing => unreachable!(),
} }
@ -249,7 +189,7 @@ impl Value {
} }
String(s) => s.as_ref().clone(), String(s) => s.as_ref().clone(),
Box(x) => x.as_ref().borrow().stringify(), Box(x) => x.as_ref().borrow().stringify(),
Fn(lfn) => format!("fn {}", lfn.name()), Fn(lfn) => lfn.get().unwrap().name.to_string(),
_ => todo!(), _ => todo!(),
} }
} }

View File

@ -57,7 +57,14 @@ impl CallFrame {
let Value::Fn(ref function) = self.function else { let Value::Fn(ref function) = self.function else {
unreachable!() unreachable!()
}; };
&function.chunk(self.arity) &function
.get()
.unwrap()
.chunks
.iter()
.find(|(arity, _)| *arity == self.arity)
.unwrap()
.1
} }
} }
@ -66,13 +73,9 @@ impl fmt::Display for CallFrame {
let Value::Fn(ref function) = self.function else { let Value::Fn(ref function) = self.function else {
unreachable!() unreachable!()
}; };
write!( let inner_fn = function.get().unwrap();
f, let name = inner_fn.name;
"CallFrame: {}/{} @ {}", write!(f, "CallFrame: {name}/{} @ {}", self.arity, self.ip)
function.name(),
self.arity,
self.ip
)
} }
} }
@ -89,13 +92,15 @@ pub struct Vm {
impl Vm { impl Vm {
pub fn new(chunk: Chunk) -> Vm { pub fn new(chunk: Chunk) -> Vm {
let lfn = LFn::Defined { let cell = OnceCell::new();
let lfn = LFn {
name: "user script", name: "user script",
doc: None, doc: None,
chunks: vec![(0, chunk)], chunks: vec![(0, chunk)],
closed: RefCell::new(vec![]), closed: vec![],
}; };
let base_fn = Value::Fn(Rc::new(lfn)); let _ = cell.set(lfn);
let base_fn = Value::Fn(Rc::new(cell));
let base_frame = CallFrame { let base_frame = CallFrame {
function: base_fn.clone(), function: base_fn.clone(),
stack_base: 0, stack_base: 0,
@ -701,28 +706,6 @@ impl Vm {
swap(&mut self.return_register[0], &mut value); swap(&mut self.return_register[0], &mut value);
self.push(value); self.push(value);
} }
Print => {
println!("{}", self.pop().show());
self.push(Value::Keyword("ok"));
self.ip += 1;
}
SetUpvalue => {
let idx = self.chunk().bytecode[self.ip + 1];
self.ip += 2;
let closed_over = self.stack[idx as usize].clone();
if let Value::Fn(lfn) = self.peek() {
lfn.close(closed_over);
}
}
GetUpvalue => {
let idx = self.chunk().bytecode[self.ip + 1];
self.ip += 2;
if let Value::Fn(ref inner) = self.frame.function {
self.push(inner.as_ref().upvalue(idx));
} else {
unreachable!();
}
}
} }
} }
} }