Compare commits
No commits in common. "61b1b7bf90a62ee5beabf5c71c6416751c61d52c" and "c497b90a16bb248c376378ac9d67248ff1255ff8" have entirely different histories.
61b1b7bf90
...
c497b90a16
11
src/base.rs
11
src/base.rs
|
@ -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"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
140
src/compiler.rs
140
src/compiler.rs
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
92
src/value.rs
92
src/value.rs
|
@ -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!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
49
src/vm.rs
49
src/vm.rs
|
@ -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!();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user