working on panics
This commit is contained in:
parent
f97f6670bd
commit
0d8b42662b
4
pkg/rudus.d.ts
vendored
4
pkg/rudus.d.ts
vendored
|
@ -14,8 +14,8 @@ export interface InitOutput {
|
|||
readonly __wbindgen_malloc: (a: number, b: number) => number;
|
||||
readonly __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number;
|
||||
readonly __wbindgen_export_6: WebAssembly.Table;
|
||||
readonly closure303_externref_shim: (a: number, b: number, c: any) => void;
|
||||
readonly closure327_externref_shim: (a: number, b: number, c: any, d: any) => void;
|
||||
readonly closure352_externref_shim: (a: number, b: number, c: any) => void;
|
||||
readonly closure375_externref_shim: (a: number, b: number, c: any, d: any) => void;
|
||||
readonly __wbindgen_start: () => void;
|
||||
}
|
||||
|
||||
|
|
|
@ -240,13 +240,13 @@ function _assertNum(n) {
|
|||
function __wbg_adapter_20(arg0, arg1, arg2) {
|
||||
_assertNum(arg0);
|
||||
_assertNum(arg1);
|
||||
wasm.closure303_externref_shim(arg0, arg1, arg2);
|
||||
wasm.closure352_externref_shim(arg0, arg1, arg2);
|
||||
}
|
||||
|
||||
function __wbg_adapter_50(arg0, arg1, arg2, arg3) {
|
||||
_assertNum(arg0);
|
||||
_assertNum(arg1);
|
||||
wasm.closure327_externref_shim(arg0, arg1, arg2, arg3);
|
||||
wasm.closure375_externref_shim(arg0, arg1, arg2, arg3);
|
||||
}
|
||||
|
||||
async function __wbg_load(module, imports) {
|
||||
|
@ -425,8 +425,8 @@ function __wbg_get_imports() {
|
|||
_assertBoolean(ret);
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbindgen_closure_wrapper7980 = function() { return logError(function (arg0, arg1, arg2) {
|
||||
const ret = makeMutClosure(arg0, arg1, 304, __wbg_adapter_20);
|
||||
imports.wbg.__wbindgen_closure_wrapper8059 = function() { return logError(function (arg0, arg1, arg2) {
|
||||
const ret = makeMutClosure(arg0, arg1, 353, __wbg_adapter_20);
|
||||
return ret;
|
||||
}, arguments) };
|
||||
imports.wbg.__wbindgen_debug_string = function(arg0, arg1) {
|
||||
|
|
Binary file not shown.
4
pkg/rudus_bg.wasm.d.ts
vendored
4
pkg/rudus_bg.wasm.d.ts
vendored
|
@ -9,6 +9,6 @@ export const __wbindgen_free: (a: number, b: number, c: number) => void;
|
|||
export const __wbindgen_malloc: (a: number, b: number) => number;
|
||||
export const __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number;
|
||||
export const __wbindgen_export_6: WebAssembly.Table;
|
||||
export const closure303_externref_shim: (a: number, b: number, c: any) => void;
|
||||
export const closure327_externref_shim: (a: number, b: number, c: any, d: any) => void;
|
||||
export const closure352_externref_shim: (a: number, b: number, c: any) => void;
|
||||
export const closure375_externref_shim: (a: number, b: number, c: any, d: any) => void;
|
||||
export const __wbindgen_start: () => void;
|
||||
|
|
18
src/chunk.rs
18
src/chunk.rs
|
@ -1,5 +1,6 @@
|
|||
use crate::op::Op;
|
||||
use crate::value::{Key, Value};
|
||||
use chumsky::prelude::SimpleSpan;
|
||||
use imbl::HashMap;
|
||||
use num_traits::FromPrimitive;
|
||||
use regex::Regex;
|
||||
|
@ -18,6 +19,9 @@ pub struct Chunk {
|
|||
pub string_patterns: Vec<StrPattern>,
|
||||
pub env: HashMap<Key, Value>,
|
||||
pub msgs: Vec<String>,
|
||||
pub spans: Vec<SimpleSpan>,
|
||||
pub src: &'static str,
|
||||
pub input: &'static str,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Chunk {
|
||||
|
@ -32,13 +36,13 @@ impl Chunk {
|
|||
use Op::*;
|
||||
match op {
|
||||
Pop | Store | Stash | Load | Nil | True | False | MatchNil | MatchTrue | MatchFalse
|
||||
| PanicIfNoMatch | ResetMatch | GetKey | PanicNoWhen | PanicNoMatch | TypeOf
|
||||
| Duplicate | Decrement | ToInt | Noop | LoadTuple | LoadList | Eq | Add | Sub
|
||||
| Mult | Div | Unbox | BoxStore | Assert | Get | At | Not | Panic | EmptyString
|
||||
| ConcatStrings | Stringify | MatchType | Return | UnconditionalMatch | Print
|
||||
| AppendList | ConcatList | PushList | PushDict | AppendDict | ConcatDict | Nothing
|
||||
| PushGlobal | SetUpvalue | LoadMessage | NextMessage | MatchMessage | ClearMessage
|
||||
| SendMethod => {
|
||||
| ResetMatch | GetKey | PanicWhenFallthrough | PanicNoMatch | PanicNoFnMatch
|
||||
| PanicNoLetMatch | TypeOf | Duplicate | Decrement | ToInt | Noop | LoadTuple
|
||||
| LoadList | Eq | Add | Sub | Mult | Div | Unbox | BoxStore | Assert | Get | At
|
||||
| Not | Panic | EmptyString | ConcatStrings | Stringify | MatchType | Return
|
||||
| UnconditionalMatch | Print | AppendList | ConcatList | PushList | PushDict
|
||||
| AppendDict | ConcatDict | Nothing | PushGlobal | SetUpvalue | LoadMessage
|
||||
| NextMessage | MatchMessage | ClearMessage | SendMethod | LoadScrutinee => {
|
||||
println!("{i:04}: {op}")
|
||||
}
|
||||
Constant | MatchConstant => {
|
||||
|
|
|
@ -40,7 +40,7 @@ impl LoopInfo {
|
|||
}
|
||||
}
|
||||
|
||||
fn get_builtin(name: &str, arity: usize) -> Option<Op> {
|
||||
fn get_builtin(_name: &str, _arity: usize) -> Option<Op> {
|
||||
// match (name, arity) {
|
||||
// ("type", 1) => Some(Op::TypeOf),
|
||||
// ("eq?", 2) => Some(Op::Eq),
|
||||
|
@ -453,13 +453,13 @@ impl Compiler {
|
|||
// return the evaluated rhs instead of whatever is last on the stack
|
||||
// we do this by pretending it's a binding
|
||||
(Let(patt, expr), _) => {
|
||||
// self.match_depth = 0;
|
||||
self.visit(expr);
|
||||
let expr_pos = self.stack_depth - 1;
|
||||
self.report_ast("let binding: matching".to_string(), patt);
|
||||
self.reset_match();
|
||||
self.emit_op(Op::LoadScrutinee);
|
||||
self.visit(patt);
|
||||
self.emit_op(Op::PanicIfNoMatch);
|
||||
self.emit_op(Op::PanicNoLetMatch);
|
||||
self.emit_op(Op::PushBinding);
|
||||
self.emit_byte(expr_pos);
|
||||
self.stack_depth += 1;
|
||||
|
@ -509,14 +509,13 @@ impl Compiler {
|
|||
}
|
||||
Let(patt, expr) => {
|
||||
self.report_depth("before let binding");
|
||||
// self.match_depth = 0;
|
||||
// self.emit_op(Op::ResetMatch);
|
||||
self.visit(expr);
|
||||
self.report_depth("after let expr");
|
||||
self.report_ast("let binding: matching".to_string(), patt);
|
||||
self.reset_match();
|
||||
self.emit_op(Op::LoadScrutinee);
|
||||
self.visit(patt);
|
||||
self.emit_op(Op::PanicIfNoMatch);
|
||||
self.emit_op(Op::PanicNoLetMatch);
|
||||
self.report_depth("after let binding");
|
||||
}
|
||||
WordPattern(name) => {
|
||||
|
@ -760,7 +759,7 @@ impl Compiler {
|
|||
match part {
|
||||
StringPart::Word(word) => {
|
||||
// println!("wordpart: {word}");
|
||||
words.push(word.clone());
|
||||
words.push(*word);
|
||||
pattern.push_str("(.*)");
|
||||
}
|
||||
StringPart::Data(data) => {
|
||||
|
@ -1012,7 +1011,7 @@ impl Compiler {
|
|||
jump_idxes.push(self.stub_jump(Op::Jump));
|
||||
self.patch_jump(jif_jump_idx, self.len() - jif_jump_idx - 3);
|
||||
}
|
||||
self.emit_op(Op::PanicNoWhen);
|
||||
self.emit_op(Op::PanicWhenFallthrough);
|
||||
for idx in jump_idxes {
|
||||
self.patch_jump(idx, self.len() - idx - 3);
|
||||
}
|
||||
|
@ -1023,6 +1022,7 @@ impl Compiler {
|
|||
let tail_pos = self.tail_pos;
|
||||
self.tail_pos = false;
|
||||
self.visit(scrutinee.as_ref());
|
||||
self.emit_op(Op::LoadScrutinee);
|
||||
let stack_depth = self.stack_depth;
|
||||
let mut jump_idxes = vec![];
|
||||
let mut clauses = clauses.iter();
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
// use crate::process::{LErr, Trace};
|
||||
use crate::lexer::Token;
|
||||
use crate::validator::VErr;
|
||||
use crate::panic::{Panic, PanicMsg};
|
||||
use crate::vm::CallFrame;
|
||||
use chumsky::error::RichPattern;
|
||||
use chumsky::prelude::*;
|
||||
|
||||
|
||||
const SEPARATOR: &str = "\n\n";
|
||||
|
||||
fn line_number(src: &'static str, span: SimpleSpan) -> usize {
|
||||
|
@ -90,3 +93,37 @@ fn parsing_message(err: Rich<'static, Token>) -> String {
|
|||
let expecteds = expecteds.join(" | ");
|
||||
format!("Ludus did not expect to see: {found}\n expected: {expecteds}")
|
||||
}
|
||||
|
||||
pub fn panic(panic: Panic, src: &'static str, input: &'static str) -> String {
|
||||
let msgs = vec!["Ludus panicked!".to_string()];
|
||||
let msg = match panic.msg {
|
||||
PanicMsg::Generic(s) => s,
|
||||
_ => "no match".to_string(),
|
||||
}
|
||||
|
||||
todo!()
|
||||
|
||||
}
|
||||
|
||||
fn traceback(_panic: Panic) -> String {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn frame_info(frame: CallFrame) -> String {
|
||||
let chunk = frame.chunk();
|
||||
let CallFrame{function, arity, ip, ..} = frame;
|
||||
|
||||
|
||||
todo!()
|
||||
}
|
||||
|
||||
|
||||
/////// Some thoughts
|
||||
// We're putting the information we need on the function and the chunk.
|
||||
// In the compiler, on functions, build up a vec of strings that are the patterns the function can match against
|
||||
// The pattern asts have a `show` method.
|
||||
// And with the additional members on Chunk, we should have everything we need for a pretty fn no match message
|
||||
// Let no match is no problem, either. We should have no concerns pulling the line with the span start and string
|
||||
// We don't need to reproduce the pattern, since it will be right there in the code
|
||||
// As for match forms, we'll just use "no match" and print the value
|
||||
|
||||
|
|
46
src/io.rs
46
src/io.rs
|
@ -1,10 +1,11 @@
|
|||
use wasm_bindgen::prelude::*;
|
||||
use serde::{Serialize, Deserialize};
|
||||
use crate::value::Value;
|
||||
use crate::vm::Panic;
|
||||
use imbl::Vector;
|
||||
use std::rc::Rc;
|
||||
|
||||
const OK: Value = Value::Keyword("ok");
|
||||
const ERR: Value = Value::Keyword("err");
|
||||
|
||||
#[wasm_bindgen(module = "/pkg/worker.js")]
|
||||
extern "C" {
|
||||
|
@ -32,41 +33,6 @@ pub enum MsgOut {
|
|||
Ready
|
||||
}
|
||||
|
||||
|
||||
// impl MsgOut {
|
||||
// pub fn to_json(&self) -> String {
|
||||
// match self {
|
||||
// MsgOut::Complete(value) => match value {
|
||||
// Ok(value) => {
|
||||
// make_json_payload("Complete", serde_json::to_string(&value.show()).unwrap())
|
||||
// },
|
||||
// Err(_) => make_json_payload("Complete", "\"null\"".to_string())
|
||||
// },
|
||||
// MsgOut::Commands(commands) => {
|
||||
// let commands = commands.as_list();
|
||||
// let vals_json = commands.iter().map(|v| v.to_json().unwrap()).collect::<Vec<_>>().join(",");
|
||||
// let vals_json = format!("[{vals_json}]");
|
||||
// make_json_payload("Commands", vals_json)
|
||||
// }
|
||||
// MsgOut::Fetch(value) => {
|
||||
// // TODO: do parsing here?
|
||||
// // Right now, defer to fetch
|
||||
// let url = value.to_json().unwrap();
|
||||
// make_json_payload("Fetch", url)
|
||||
// }
|
||||
// MsgOut::Console(lines) => {
|
||||
// let lines = lines.as_list();
|
||||
// let json_lines = lines.iter().map(|line| line.to_json().unwrap()).collect::<Vec<_>>().join("\\n");
|
||||
// let json_lines = format!("\"{json_lines}\"");
|
||||
// make_json_payload("Console", json_lines)
|
||||
// }
|
||||
// MsgOut::Ready => {
|
||||
// make_json_payload("Ready", "\"null\"".to_string())
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(tag = "verb", content = "data")]
|
||||
pub enum MsgIn {
|
||||
|
@ -88,7 +54,7 @@ impl std::fmt::Display for MsgIn {
|
|||
}
|
||||
|
||||
impl MsgIn {
|
||||
pub fn to_value(self) -> Value {
|
||||
pub fn into_value(self) -> Value {
|
||||
match self {
|
||||
MsgIn::Input(str) => Value::string(str),
|
||||
MsgIn::Fetch(url, status_f64, string) => {
|
||||
|
@ -96,9 +62,9 @@ impl MsgIn {
|
|||
let status = Value::Number(status_f64);
|
||||
let text = Value::string(string);
|
||||
let result_tuple = if status_f64 == 200.0 {
|
||||
Value::tuple(vec![Value::keyword("ok".to_string()), text])
|
||||
Value::tuple(vec![OK, text])
|
||||
} else {
|
||||
Value::tuple(vec![Value::keyword("err".to_string()), status])
|
||||
Value::tuple(vec![ERR, status])
|
||||
};
|
||||
Value::tuple(vec![url, result_tuple])
|
||||
}
|
||||
|
@ -122,13 +88,11 @@ pub async fn send_err_to_ludus_console(msg: String) {
|
|||
pub async fn do_io (msgs: Vec<MsgOut>) -> Vec<MsgIn> {
|
||||
let json = serde_json::to_string(&msgs).unwrap();
|
||||
let inbox = io (json).await;
|
||||
// if our request dies, make sure we return back to the event loop
|
||||
let inbox = match inbox {
|
||||
Ok(msgs) => msgs,
|
||||
Err(_) => return vec![]
|
||||
};
|
||||
let inbox = inbox.as_string().expect("response should be a string");
|
||||
// log(format!("got a message: {inbox}"));
|
||||
let inbox: Vec<MsgIn> = serde_json::from_str(inbox.as_str()).expect("response from js should be valid");
|
||||
if !inbox.is_empty() {
|
||||
log("ludus received messages".to_string());
|
||||
|
|
20
src/lib.rs
20
src/lib.rs
|
@ -40,6 +40,8 @@ use crate::validator::Validator;
|
|||
mod errors;
|
||||
use crate::errors::{lexing, parsing, validation};
|
||||
|
||||
mod panic;
|
||||
|
||||
mod chunk;
|
||||
mod op;
|
||||
|
||||
|
@ -179,16 +181,16 @@ pub async fn ludus(src: String) {
|
|||
|
||||
let mut world = World::new(vm_chunk, prelude.clone(), DEBUG_SCRIPT_RUN);
|
||||
world.run().await;
|
||||
let result = world.result.clone();
|
||||
// let result = world.result.clone();
|
||||
|
||||
// TODO: actually do something useful on a panic
|
||||
match result {
|
||||
Some(Ok(val)) => val.show(),
|
||||
Some(Err(panic)) => format!("Ludus panicked! {panic}"),
|
||||
None => "Ludus run terminated by user".to_string()
|
||||
};
|
||||
if DEBUG_SCRIPT_RUN {
|
||||
// vm.print_stack();
|
||||
}
|
||||
// match result {
|
||||
// Some(Ok(val)) => val.show(),
|
||||
// Some(Err(panic)) => format!("Ludus panicked! {panic}"),
|
||||
// None => "Ludus run terminated by user".to_string()
|
||||
// };
|
||||
// if DEBUG_SCRIPT_RUN {
|
||||
// // vm.print_stack();
|
||||
// }
|
||||
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use rudus::value::Value;
|
||||
use std::env;
|
||||
|
||||
pub fn main() {
|
||||
env::set_var("RUST_BACKTRACE", "1");
|
||||
println!("Hello, world.")
|
||||
}
|
||||
|
|
62
src/op.rs
62
src/op.rs
|
@ -25,7 +25,6 @@ pub enum Op {
|
|||
MatchNil,
|
||||
MatchTrue,
|
||||
MatchFalse,
|
||||
PanicIfNoMatch,
|
||||
MatchConstant,
|
||||
MatchString,
|
||||
PushStringMatches,
|
||||
|
@ -51,10 +50,12 @@ pub enum Op {
|
|||
DropDictEntry,
|
||||
PushBox,
|
||||
GetKey,
|
||||
PanicNoWhen,
|
||||
PanicWhenFallthrough,
|
||||
JumpIfNoMatch,
|
||||
JumpIfMatch,
|
||||
PanicNoMatch,
|
||||
PanicNoLetMatch,
|
||||
PanicNoFnMatch,
|
||||
TypeOf,
|
||||
JumpBack,
|
||||
JumpIfZero,
|
||||
|
@ -82,7 +83,17 @@ pub enum Op {
|
|||
Assert,
|
||||
Get,
|
||||
At,
|
||||
|
||||
// Inc,
|
||||
// Dec,
|
||||
// Gt,
|
||||
// Gte,
|
||||
// Lt,
|
||||
// Lte,
|
||||
// Mod,
|
||||
// First,
|
||||
// Rest
|
||||
// Sqrt,
|
||||
// Append,
|
||||
Not,
|
||||
Print,
|
||||
SetUpvalue,
|
||||
|
@ -95,44 +106,8 @@ pub enum Op {
|
|||
MatchMessage,
|
||||
ClearMessage,
|
||||
SendMethod,
|
||||
// Inc,
|
||||
// Dec,
|
||||
// Gt,
|
||||
// Gte,
|
||||
// Lt,
|
||||
// Lte,
|
||||
// Mod,
|
||||
// Round,
|
||||
// Ceil,
|
||||
// Floor,
|
||||
// Random,
|
||||
// Sqrt,
|
||||
|
||||
// Assoc,
|
||||
// Concat,
|
||||
// Conj,
|
||||
// Count,
|
||||
// Disj,
|
||||
// Dissoc,
|
||||
// Range,
|
||||
// Rest,
|
||||
// Slice,
|
||||
|
||||
// "atan_2" math/atan2
|
||||
// "chars" chars
|
||||
// "cos" math/cos
|
||||
// "doc" doc
|
||||
// "downcase" string/ascii-lower
|
||||
// "pi" math/pi
|
||||
// "show" show
|
||||
// "sin" math/sin
|
||||
// "split" string/split
|
||||
// "str_slice" string/slice
|
||||
// "tan" math/tan
|
||||
// "trim" string/trim
|
||||
// "triml" string/triml
|
||||
// "trimr" string/trimr
|
||||
// "upcase" string/ascii-upper
|
||||
LoadScrutinee,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Op {
|
||||
|
@ -163,7 +138,6 @@ impl std::fmt::Display for Op {
|
|||
MatchTrue => "match_true",
|
||||
MatchFalse => "match_false",
|
||||
ResetMatch => "reset_match",
|
||||
PanicIfNoMatch => "panic_if_no_match",
|
||||
MatchConstant => "match_constant",
|
||||
MatchString => "match_string",
|
||||
PushStringMatches => "push_string_matches",
|
||||
|
@ -189,10 +163,12 @@ impl std::fmt::Display for Op {
|
|||
DropDictEntry => "drop_dict_entry",
|
||||
PushBox => "push_box",
|
||||
GetKey => "get_key",
|
||||
PanicNoWhen => "panic_no_when",
|
||||
PanicWhenFallthrough => "panic_no_when",
|
||||
JumpIfNoMatch => "jump_if_no_match",
|
||||
JumpIfMatch => "jump_if_match",
|
||||
PanicNoMatch => "panic_no_match",
|
||||
PanicNoFnMatch => "panic_no_fn_match",
|
||||
PanicNoLetMatch => "panic_no_let_match",
|
||||
TypeOf => "type_of",
|
||||
JumpBack => "jump_back",
|
||||
JumpIfZero => "jump_if_zero",
|
||||
|
@ -232,6 +208,8 @@ impl std::fmt::Display for Op {
|
|||
MatchMessage => "match_message",
|
||||
ClearMessage => "clear_message",
|
||||
SendMethod => "send_method",
|
||||
|
||||
LoadScrutinee => "load_scrutinee",
|
||||
};
|
||||
write!(f, "{rep}")
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@ use imbl::{HashMap, Vector};
|
|||
use serde::ser::{Serialize, SerializeMap, SerializeSeq, Serializer};
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
use wasm_bindgen::JsValue;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum LFn {
|
||||
|
@ -525,10 +524,6 @@ impl Value {
|
|||
Value::String(Rc::new(str))
|
||||
}
|
||||
|
||||
pub fn keyword(str: String) -> Value {
|
||||
Value::Keyword(str.leak())
|
||||
}
|
||||
|
||||
pub fn list(list: Vector<Value>) -> Value {
|
||||
Value::List(Box::new(list))
|
||||
}
|
||||
|
|
317
src/vm.rs
317
src/vm.rs
|
@ -1,8 +1,7 @@
|
|||
use crate::ast::Ast;
|
||||
use crate::base::BaseFn;
|
||||
use crate::chunk::Chunk;
|
||||
use crate::op::Op;
|
||||
use crate::spans::Spanned;
|
||||
use crate::panic::{Panic, PanicMsg};
|
||||
use crate::value::{Key, LFn, Value};
|
||||
use crate::world::Zoo;
|
||||
use imbl::{HashMap, Vector};
|
||||
|
@ -15,31 +14,6 @@ use std::rc::Rc;
|
|||
|
||||
const MAX_REDUCTIONS: usize = 1000;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Panic {
|
||||
Str(&'static str),
|
||||
String(String),
|
||||
}
|
||||
|
||||
impl fmt::Display for Panic {
|
||||
fn fmt(self: &Panic, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Panic::Str(msg) => write!(f, "{msg}"),
|
||||
Panic::String(msg) => write!(f, "{msg}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct Trace {
|
||||
pub callee: Spanned<Ast>,
|
||||
pub caller: Spanned<Ast>,
|
||||
pub function: Value,
|
||||
pub arguments: Value,
|
||||
pub input: &'static str,
|
||||
pub src: &'static str,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct CallFrame {
|
||||
pub function: Value,
|
||||
|
@ -81,22 +55,23 @@ const REGISTER_SIZE: usize = 8;
|
|||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct Creature {
|
||||
pub stack: Vec<Value>,
|
||||
pub call_stack: Vec<CallFrame>,
|
||||
pub frame: CallFrame,
|
||||
pub ip: usize,
|
||||
pub register: [Value; REGISTER_SIZE],
|
||||
pub matches: bool,
|
||||
pub match_depth: u8,
|
||||
stack: Vec<Value>,
|
||||
call_stack: Vec<CallFrame>,
|
||||
frame: CallFrame,
|
||||
ip: usize,
|
||||
register: [Value; REGISTER_SIZE],
|
||||
matches: bool,
|
||||
match_depth: u8,
|
||||
pub result: Option<Result<Value, Panic>>,
|
||||
debug: bool,
|
||||
last_code: usize,
|
||||
pub pid: &'static str,
|
||||
pub mbx: VecDeque<Value>,
|
||||
msg_idx: usize,
|
||||
pub reductions: usize,
|
||||
pub zoo: Rc<RefCell<Zoo>>,
|
||||
pub r#yield: bool,
|
||||
reductions: usize,
|
||||
zoo: Rc<RefCell<Zoo>>,
|
||||
r#yield: bool,
|
||||
scrutinee: Option<Value>,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Creature {
|
||||
|
@ -143,6 +118,7 @@ impl Creature {
|
|||
reductions: 0,
|
||||
r#yield: false,
|
||||
msg_idx: 0,
|
||||
scrutinee: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -209,39 +185,48 @@ impl Creature {
|
|||
self.chunk().dissasemble_instr(&mut ip);
|
||||
}
|
||||
|
||||
// pub fn run(&mut self) -> &Result<Value, Panic> {
|
||||
// while self.result.is_none() {
|
||||
// self.interpret();
|
||||
// pub fn call_stack(&mut self) -> String {
|
||||
// let mut stack = format!(" calling {}", self.frame.function.show());
|
||||
// for frame in self.call_stack.iter().rev() {
|
||||
// let mut name = frame.function.show();
|
||||
// name = if name == "fn user script" {
|
||||
// "user script".to_string()
|
||||
// } else {
|
||||
// name
|
||||
// };
|
||||
// stack = format!("{stack}\n from {name}");
|
||||
// }
|
||||
// self.result.as_ref().unwrap()
|
||||
// stack
|
||||
// }
|
||||
|
||||
pub fn call_stack(&mut self) -> String {
|
||||
let mut stack = format!(" calling {}", self.frame.function.show());
|
||||
for frame in self.call_stack.iter().rev() {
|
||||
let mut name = frame.function.show();
|
||||
name = if name == "fn user script" {
|
||||
"user script".to_string()
|
||||
} else {
|
||||
name
|
||||
// pub fn panic(&mut self, msg: &'static str) {
|
||||
// let msg = format!("{msg}\nPanic traceback:\n{}", self.call_stack());
|
||||
// println!("process {} panicked!\n{msg}", self.pid);
|
||||
// self.result = Some(Err(Panic::String(msg)));
|
||||
// self.r#yield = true;
|
||||
// }
|
||||
|
||||
// pub fn panic_with(&mut self, msg: String) {
|
||||
// let msg = format!("{msg}\nPanic traceback:\n{}", self.call_stack());
|
||||
// println!("process {} panicked!\n{msg}", self.pid);
|
||||
// self.result = Some(Err(Panic::String(msg)));
|
||||
// self.r#yield = true;
|
||||
// }
|
||||
|
||||
fn panic(&mut self, msg: PanicMsg) {
|
||||
let panic = Panic {
|
||||
msg,
|
||||
frame: self.frame.clone(),
|
||||
scrutinee: self.scrutinee.clone(),
|
||||
ip: self.ip,
|
||||
call_stack: self.call_stack.clone(),
|
||||
};
|
||||
stack = format!("{stack}\n from {name}");
|
||||
}
|
||||
stack
|
||||
}
|
||||
|
||||
pub fn panic(&mut self, msg: &'static str) {
|
||||
let msg = format!("{msg}\nPanic traceback:\n{}", self.call_stack());
|
||||
println!("process {} panicked!\n{msg}", self.pid);
|
||||
self.result = Some(Err(Panic::String(msg)));
|
||||
self.result = Some(Err(panic));
|
||||
self.r#yield = true;
|
||||
}
|
||||
|
||||
pub fn panic_with(&mut self, msg: String) {
|
||||
let msg = format!("{msg}\nPanic traceback:\n{}", self.call_stack());
|
||||
println!("process {} panicked!\n{msg}", self.pid);
|
||||
self.result = Some(Err(Panic::String(msg)));
|
||||
self.r#yield = true;
|
||||
fn panic_with(&mut self, msg: String) {
|
||||
self.panic(PanicMsg::Generic(msg));
|
||||
}
|
||||
|
||||
fn get_value_at(&mut self, idx: u8) -> Value {
|
||||
|
@ -286,31 +271,11 @@ impl Creature {
|
|||
fn handle_msg(&mut self, args: Vec<Value>) {
|
||||
println!("message received by {}: {}", self.pid, args[0]);
|
||||
let Value::Keyword(msg) = args.first().unwrap() else {
|
||||
return self.panic("malformed message to Process");
|
||||
return self.panic_with("malformed message to Process".to_string());
|
||||
};
|
||||
match *msg {
|
||||
"self" => self.push(Value::Keyword(self.pid)),
|
||||
"send" => {
|
||||
self.send_msg(args[1].clone(), args[2].clone())
|
||||
// let Value::Keyword(pid) = args[1] else {
|
||||
// return self.panic("malformed pid");
|
||||
// };
|
||||
// println!(
|
||||
// "sending msg from {} to {} of {}",
|
||||
// self.pid,
|
||||
// pid,
|
||||
// args[2].show()
|
||||
// );
|
||||
// if self.pid == pid {
|
||||
// self.mbx.push_back(args[2].clone());
|
||||
// } else {
|
||||
// self.zoo
|
||||
// .as_ref()
|
||||
// .borrow_mut()
|
||||
// .send_msg(pid, args[2].clone());
|
||||
// }
|
||||
// self.push(Value::Keyword("ok"));
|
||||
}
|
||||
"send" => self.send_msg(args[1].clone(), args[2].clone()),
|
||||
"spawn" => {
|
||||
let f = args[1].clone();
|
||||
let proc = Creature::spawn(f, self.zoo.clone(), self.debug);
|
||||
|
@ -358,22 +323,6 @@ impl Creature {
|
|||
self.r#yield = true;
|
||||
self.push(Value::Keyword("ok"));
|
||||
}
|
||||
// "flush_i" => {
|
||||
// let Value::Number(n) = args[1] else {
|
||||
// unreachable!()
|
||||
// };
|
||||
// println!("flushing message at {n}");
|
||||
// self.mbx.remove(n as usize);
|
||||
// println!(
|
||||
// "mailbox is now: {}",
|
||||
// self.mbx
|
||||
// .iter()
|
||||
// .map(|msg| msg.to_string())
|
||||
// .collect::<Vec<_>>()
|
||||
// .join(" | ")
|
||||
// );
|
||||
// self.push(Value::Keyword("ok"));
|
||||
// }
|
||||
msg => panic!("Process does not understand message: {msg}"),
|
||||
}
|
||||
}
|
||||
|
@ -457,7 +406,10 @@ impl Creature {
|
|||
match cond {
|
||||
Value::Number(x) if x <= 0.0 => self.ip += jump_len,
|
||||
Value::Number(..) => (),
|
||||
_ => return self.panic("repeat requires a number"),
|
||||
_ => {
|
||||
return self
|
||||
.panic_with(format!("repeat requires a number, but got {cond}"))
|
||||
}
|
||||
}
|
||||
}
|
||||
Pop => {
|
||||
|
@ -533,9 +485,19 @@ impl Creature {
|
|||
let value = self.get_scrutinee();
|
||||
self.matches = value == Value::False;
|
||||
}
|
||||
PanicIfNoMatch => {
|
||||
PanicNoMatch => {
|
||||
if !self.matches {
|
||||
return self.panic("no match");
|
||||
return self.panic(PanicMsg::NoMatch);
|
||||
}
|
||||
}
|
||||
PanicNoLetMatch => {
|
||||
if !self.matches {
|
||||
return self.panic(PanicMsg::NoLetMatch);
|
||||
}
|
||||
}
|
||||
PanicNoFnMatch => {
|
||||
if !self.matches {
|
||||
return self.panic(PanicMsg::NoFnMatch);
|
||||
}
|
||||
}
|
||||
MatchConstant => {
|
||||
|
@ -611,14 +573,18 @@ impl Creature {
|
|||
self.push(member.clone());
|
||||
}
|
||||
}
|
||||
_ => return self.panic("internal error: expected tuple"),
|
||||
_ => {
|
||||
return self
|
||||
.panic_with(format!("internal error: expected tuple, got {tuple}"))
|
||||
}
|
||||
};
|
||||
}
|
||||
LoadSplattedTuple => {
|
||||
let load_len = self.read() as usize;
|
||||
let tuple = self.get_scrutinee();
|
||||
let Value::Tuple(members) = tuple else {
|
||||
return self.panic("internal error: expected tuple");
|
||||
return self
|
||||
.panic_with(format!("internal error: expected tuple, got {tuple}"));
|
||||
};
|
||||
for i in 0..load_len - 1 {
|
||||
self.push(members[i].clone());
|
||||
|
@ -635,20 +601,24 @@ impl Creature {
|
|||
AppendList => {
|
||||
let value = self.pop();
|
||||
let list = self.pop();
|
||||
let Value::List(mut list) = list else {
|
||||
return self.panic("only lists may be splatted into lists");
|
||||
let Value::List(mut members) = list else {
|
||||
return self.panic_with(format!(
|
||||
"only lists may be splatted into lists, but got {list}"
|
||||
));
|
||||
};
|
||||
list.push_back(value);
|
||||
self.push(Value::List(list));
|
||||
members.push_back(value);
|
||||
self.push(Value::List(members));
|
||||
}
|
||||
ConcatList => {
|
||||
let splatted = self.pop();
|
||||
let list = self.pop();
|
||||
let target = self.pop();
|
||||
let Value::List(mut target) = target else {
|
||||
unreachable!()
|
||||
};
|
||||
let Value::List(splatted) = splatted else {
|
||||
return self.panic("only lists may be splatted into lists");
|
||||
let Value::List(splatted) = list else {
|
||||
return self.panic_with(format!(
|
||||
"only lists may be splatted into lists, but got {list}"
|
||||
));
|
||||
};
|
||||
target.append(*splatted);
|
||||
self.push(Value::List(target));
|
||||
|
@ -680,14 +650,18 @@ impl Creature {
|
|||
self.push(member.clone());
|
||||
}
|
||||
}
|
||||
_ => return self.panic("internal error: expected list"),
|
||||
_ => {
|
||||
return self
|
||||
.panic_with(format!("internal error: expected list, got {list}"))
|
||||
}
|
||||
};
|
||||
}
|
||||
LoadSplattedList => {
|
||||
let loaded_len = self.read() as usize;
|
||||
let list = self.get_scrutinee();
|
||||
let Value::List(members) = list else {
|
||||
return self.panic("internal error: expected list");
|
||||
return self
|
||||
.panic_with(format!("internal error: expected list, got {list}"));
|
||||
};
|
||||
for i in 0..loaded_len - 1 {
|
||||
self.push(members[i].clone());
|
||||
|
@ -708,8 +682,11 @@ impl Creature {
|
|||
self.push(Value::Dict(dict));
|
||||
}
|
||||
ConcatDict => {
|
||||
let Value::Dict(splatted) = self.pop() else {
|
||||
return self.panic("only dicts may be splatted into dicts");
|
||||
let prolly_dict = self.pop();
|
||||
let Value::Dict(splatted) = prolly_dict else {
|
||||
return self.panic_with(format!(
|
||||
"only dicts may be splatted into dicts, got {prolly_dict}"
|
||||
));
|
||||
};
|
||||
let Value::Dict(target) = self.pop() else {
|
||||
unreachable!()
|
||||
|
@ -795,7 +772,7 @@ impl Creature {
|
|||
if let Value::Number(x) = val {
|
||||
self.push(Value::Number(x as usize as f64));
|
||||
} else {
|
||||
return self.panic("repeat requires a number");
|
||||
return self.panic_with(format!("repeat requires a number, but got {val}"));
|
||||
}
|
||||
}
|
||||
Decrement => {
|
||||
|
@ -803,7 +780,8 @@ impl Creature {
|
|||
if let Value::Number(x) = val {
|
||||
self.push(Value::Number(x - 1.0));
|
||||
} else {
|
||||
return self.panic("you may only decrement a number");
|
||||
return self
|
||||
.panic_with(format!("you may only decrement a number, but got {val}"));
|
||||
}
|
||||
}
|
||||
Duplicate => {
|
||||
|
@ -812,8 +790,10 @@ impl Creature {
|
|||
MatchDepth => {
|
||||
self.match_depth = self.read();
|
||||
}
|
||||
PanicNoWhen | PanicNoMatch => {
|
||||
return self.panic("no match");
|
||||
PanicWhenFallthrough => {
|
||||
return self.panic_with(
|
||||
"when form fallthrough: expected one clause to be truthy".to_string(),
|
||||
);
|
||||
}
|
||||
Eq => {
|
||||
let first = self.pop();
|
||||
|
@ -827,40 +807,48 @@ impl Creature {
|
|||
Add => {
|
||||
let first = self.pop();
|
||||
let second = self.pop();
|
||||
if let (Value::Number(x), Value::Number(y)) = (first, second) {
|
||||
if let (Value::Number(x), Value::Number(y)) = (first.clone(), second.clone()) {
|
||||
self.push(Value::Number(x + y))
|
||||
} else {
|
||||
return self.panic("`add` requires two numbers");
|
||||
return self.panic_with(format!(
|
||||
"`add` requires two numbers, but got {second}, {first}"
|
||||
));
|
||||
}
|
||||
}
|
||||
Sub => {
|
||||
let first = self.pop();
|
||||
let second = self.pop();
|
||||
if let (Value::Number(x), Value::Number(y)) = (first, second) {
|
||||
if let (Value::Number(x), Value::Number(y)) = (first.clone(), second.clone()) {
|
||||
self.push(Value::Number(y - x))
|
||||
} else {
|
||||
return self.panic("`sub` requires two numbers");
|
||||
return self.panic_with(format!(
|
||||
"`sub` requires two numbers, but got {second}, {first}"
|
||||
));
|
||||
}
|
||||
}
|
||||
Mult => {
|
||||
let first = self.pop();
|
||||
let second = self.pop();
|
||||
if let (Value::Number(x), Value::Number(y)) = (first, second) {
|
||||
if let (Value::Number(x), Value::Number(y)) = (first.clone(), second.clone()) {
|
||||
self.push(Value::Number(x * y))
|
||||
} else {
|
||||
return self.panic("`mult` requires two numbers");
|
||||
return self.panic_with(format!(
|
||||
"`mult` requires two numbers, but got {second}, {first}"
|
||||
));
|
||||
}
|
||||
}
|
||||
Div => {
|
||||
let first = self.pop();
|
||||
let second = self.pop();
|
||||
if let (Value::Number(x), Value::Number(y)) = (first, second) {
|
||||
if let (Value::Number(x), Value::Number(y)) = (first.clone(), second.clone()) {
|
||||
if x == 0.0 {
|
||||
return self.panic("division by 0");
|
||||
return self.panic_with("division by 0".to_string());
|
||||
}
|
||||
self.push(Value::Number(y / x))
|
||||
} else {
|
||||
return self.panic("`div` requires two numbers");
|
||||
return self.panic_with(format!(
|
||||
"`div` requires two numbers, but got {second}, {first}"
|
||||
));
|
||||
}
|
||||
}
|
||||
Unbox => {
|
||||
|
@ -868,7 +856,8 @@ impl Creature {
|
|||
let inner = if let Value::Box(b) = the_box {
|
||||
b.borrow().clone()
|
||||
} else {
|
||||
return self.panic("`unbox` requires a box");
|
||||
return self
|
||||
.panic_with(format!("`unbox` requires a box, but got {the_box}"));
|
||||
};
|
||||
self.push(inner);
|
||||
}
|
||||
|
@ -878,14 +867,15 @@ impl Creature {
|
|||
if let Value::Box(b) = the_box {
|
||||
b.replace(new_value.clone());
|
||||
} else {
|
||||
return self.panic("`store` requires a box");
|
||||
return self
|
||||
.panic_with(format!("`store` requires a box, but got {the_box}"));
|
||||
}
|
||||
self.push(new_value);
|
||||
}
|
||||
Assert => {
|
||||
let value = self.stack.last().unwrap();
|
||||
if let Value::Nil | Value::False = value {
|
||||
return self.panic("asserted falsy value");
|
||||
return self.panic_with("asserted falsy value".to_string());
|
||||
}
|
||||
}
|
||||
Get => {
|
||||
|
@ -894,7 +884,9 @@ impl Creature {
|
|||
key,
|
||||
Value::Keyword(_) | Value::String(_) | Value::Interned(_)
|
||||
) {
|
||||
return self.panic("keys must be keywords");
|
||||
return self.panic_with(format!(
|
||||
"dict keys must be keywords or strings, but got {key}"
|
||||
));
|
||||
}
|
||||
let key = Key::from_value(key);
|
||||
let dict = self.pop();
|
||||
|
@ -907,7 +899,7 @@ impl Creature {
|
|||
At => {
|
||||
let idx = self.pop();
|
||||
let ordered = self.pop();
|
||||
let value = match (ordered, idx) {
|
||||
let value = match (ordered, idx.clone()) {
|
||||
(Value::List(l), Value::Number(i)) => {
|
||||
l.get(i as usize).unwrap_or(&Value::Nil).clone()
|
||||
}
|
||||
|
@ -915,7 +907,10 @@ impl Creature {
|
|||
t.get(i as usize).unwrap_or(&Value::Nil).clone()
|
||||
}
|
||||
(_, Value::Number(_)) => Value::Nil,
|
||||
_ => return self.panic("indexes must be numbers"),
|
||||
_ => {
|
||||
return self
|
||||
.panic_with(format!("indexes must be numbers, but got {idx}"))
|
||||
}
|
||||
};
|
||||
self.push(value);
|
||||
}
|
||||
|
@ -958,7 +953,9 @@ impl Creature {
|
|||
let arity = self.read();
|
||||
let the_fn = self.pop();
|
||||
let Value::Fn(ref inner) = the_fn else {
|
||||
return self.panic("only functions may be partially applied");
|
||||
return self.panic_with(format!(
|
||||
"only functions may be partially applied, but got {the_fn}"
|
||||
));
|
||||
};
|
||||
let args = self.stack.split_off(self.stack.len() - arity as usize);
|
||||
let partial = crate::value::Partial {
|
||||
|
@ -997,7 +994,13 @@ impl Creature {
|
|||
for i in 0..arity as usize {
|
||||
self.register[arity as usize - i - 1] = self.pop();
|
||||
}
|
||||
// self.print_stack();
|
||||
|
||||
// save the arguments as our scrutinee
|
||||
let mut scrutinee = vec![];
|
||||
for i in 0..arity as usize {
|
||||
scrutinee.push(self.register[i].clone())
|
||||
}
|
||||
self.scrutinee = Some(Value::tuple(scrutinee));
|
||||
|
||||
// then pop everything back to the current stack frame
|
||||
self.stack.truncate(self.frame.stack_base);
|
||||
|
@ -1051,20 +1054,13 @@ impl Creature {
|
|||
let x = &self.pop();
|
||||
f(x, y, z)
|
||||
}
|
||||
_ => return self.panic("internal ludus error"),
|
||||
_ => {
|
||||
return self.panic_with(
|
||||
"internal ludus error: bad base fn call".to_string(),
|
||||
)
|
||||
}
|
||||
};
|
||||
// // algo:
|
||||
// // clear the stack
|
||||
// self.stack.truncate(self.frame.stack_base);
|
||||
// // then pop back out to the enclosing stack frame
|
||||
// self.frame = self.call_stack.pop().unwrap();
|
||||
// self.ip = self.frame.ip;
|
||||
// // finally, throw the value on the stack
|
||||
self.push(value);
|
||||
// println!(
|
||||
// "=== returning to {} ===",
|
||||
// self.frame.function.as_fn().name()
|
||||
// );
|
||||
}
|
||||
Value::Partial(partial) => {
|
||||
let last_arg = self.pop();
|
||||
|
@ -1117,6 +1113,7 @@ impl Creature {
|
|||
called.show()
|
||||
));
|
||||
}
|
||||
|
||||
let splat_arity = called.as_fn().splat_arity();
|
||||
if splat_arity > 0 && arity >= splat_arity {
|
||||
let splatted_args = self.stack.split_off(
|
||||
|
@ -1125,11 +1122,22 @@ impl Creature {
|
|||
let gathered_args = Vector::from(splatted_args);
|
||||
self.push(Value::List(Box::new(gathered_args)));
|
||||
}
|
||||
|
||||
let mut scrutinee = vec![];
|
||||
for i in 0..arity {
|
||||
scrutinee.push(
|
||||
self.stack[self.stack.len() - arity as usize + i as usize]
|
||||
.clone(),
|
||||
)
|
||||
}
|
||||
self.scrutinee = Some(Value::tuple(scrutinee));
|
||||
|
||||
let arity = if splat_arity > 0 {
|
||||
splat_arity.min(arity)
|
||||
} else {
|
||||
arity
|
||||
};
|
||||
|
||||
let mut frame = CallFrame {
|
||||
function: called,
|
||||
arity,
|
||||
|
@ -1158,7 +1166,11 @@ impl Creature {
|
|||
let x = &self.pop();
|
||||
f(x, y, z)
|
||||
}
|
||||
_ => return self.panic("internal ludus error"),
|
||||
_ => {
|
||||
return self.panic_with(
|
||||
"internal ludus error: bad base fn call".to_string(),
|
||||
)
|
||||
}
|
||||
};
|
||||
self.push(value);
|
||||
}
|
||||
|
@ -1258,6 +1270,9 @@ impl Creature {
|
|||
}
|
||||
self.send_msg(target, Value::tuple(msg));
|
||||
}
|
||||
LoadScrutinee => {
|
||||
self.scrutinee = Some(self.peek().clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use crate::chunk::Chunk;
|
||||
use crate::value::{Value, Key};
|
||||
use crate::vm::{Creature, Panic};
|
||||
use crate::vm::Creature;
|
||||
use crate::panic::Panic;
|
||||
use crate::io::{MsgOut, MsgIn, do_io};
|
||||
use std::cell::RefCell;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
@ -455,7 +456,7 @@ impl World {
|
|||
match msg {
|
||||
MsgIn::Input(str) => self.fill_input(str),
|
||||
MsgIn::Kill => self.kill_signal = true,
|
||||
MsgIn::Fetch(..) => self.fetch_reply(msg.to_value()),
|
||||
MsgIn::Fetch(..) => self.fetch_reply(msg.into_value()),
|
||||
_ => todo!()
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user