Skip to main content

kb/
vm.rs

1use super::ArgSpec;
2use super::ArithmeticBinop;
3use super::ArithmeticUnop;
4use super::Binop;
5use super::Code;
6use super::Func;
7use super::GenObjPtr;
8use super::Handler;
9use super::Mark;
10use super::Opcode;
11use super::RcStr;
12use super::Unop;
13use super::Val;
14use super::Var;
15use std::cell::RefCell;
16use std::collections::HashMap;
17use std::rc::Rc;
18
19pub struct Vm<H: Handler> {
20    scope: Scope,
21    handler: H,
22}
23
24impl<H: Handler> Vm<H> {
25    pub fn new(handler: H) -> Self {
26        Self {
27            scope: Scope::new(),
28            handler,
29        }
30    }
31    pub fn exec(&mut self, code: &Code) -> Result<(), Val> {
32        for var in code.vars() {
33            self.scope.globals.insert(var.name.clone(), Val::Nil);
34        }
35        exec(&mut self.scope, &mut self.handler, code)?;
36        Ok(())
37    }
38    pub fn run_tests(&mut self, prefixes: &Vec<RcStr>) -> Result<(), Val> {
39        let tests = std::mem::replace(&mut self.scope.tests, vec![]);
40        for test in tests {
41            if prefixes
42                .iter()
43                .any(|prefix| test.name().starts_with(prefix.as_ref()))
44            {
45                print!("  test {}... ", test.name());
46                callfunc(&mut self.scope, &mut self.handler, &test, vec![])?;
47                println!("ok");
48            }
49        }
50        Ok(())
51    }
52    pub fn exec_and_run_tests(&mut self, code: &Code, prefixes: &Vec<RcStr>) -> Result<(), Val> {
53        self.exec(code)?;
54        self.run_tests(prefixes)
55    }
56    pub fn trace(&self) -> &Vec<Mark> {
57        &self.scope.trace
58    }
59}
60
61pub fn callfunc<H: Handler>(
62    scope: &mut Scope,
63    handler: &mut H,
64    func: &Code,
65    mut args: Vec<Val>,
66) -> Result<Val, Val> {
67    // match args against parameters
68    let spec = func.argspec();
69    prepare_args(spec, &mut args)?;
70
71    // initialize args: save the values into local vars
72    scope.push(func.vars());
73    for (i, arg) in args.into_iter().enumerate() {
74        scope.set(VarScope::Local, i as u32, arg);
75    }
76    let ret = exec(scope, handler, func)?;
77    scope.pop();
78    Ok(ret)
79}
80
81fn mkgenobj(func: Rc<Code>, mut args: Vec<Val>) -> Result<Val, Val> {
82    prepare_args(func.argspec(), &mut args)?;
83    let mut locals = new_locals_from_vars(func.vars());
84    for (i, arg) in args.into_iter().enumerate() {
85        locals.set_by_index(i as u32, arg);
86    }
87    let genobj = GenObj {
88        code: func,
89        locals,
90        i: 0,
91        stack: vec![],
92    };
93
94    Ok(Val::GenObj(GenObjPtr(Rc::new(RefCell::new(genobj)))))
95}
96
97fn prepare_args(spec: &ArgSpec, args: &mut Vec<Val>) -> Result<(), Val> {
98    if spec.var.is_some() || spec.def.len() > 0 {
99        // has variadic parameter or at least one default parameter
100        let argc = args.len();
101        let argmin = spec.req.len();
102        let argmax = argmin + spec.def.len();
103        if spec.var.is_some() {
104            if argc < argmin {
105                return Err(Val::String(
106                    format!("Expected at least {} args but got {}", argmin, argc).into(),
107                ));
108            }
109        } else {
110            if argc < argmin || argc > argmax {
111                return Err(Val::String(
112                    format!("Expected {} to {} args but got {}", argmin, argmax, argc).into(),
113                ));
114            }
115        }
116        let def = &spec.def;
117        for i in argc..argmax {
118            let default_val = def[i - argmin].1.clone();
119            args.push(default_val);
120        }
121        if spec.var.is_some() {
122            let rem_args: Vec<Val> = if argmax < argc {
123                args.drain(argmax..argc).collect()
124            } else {
125                vec![]
126            };
127            args.push(rem_args.into());
128        }
129    } else {
130        // no variadic parameter and no default parameters
131        if args.len() != spec.req.len() {
132            return Err(Val::String(
133                format!("Expected {} args but got {}", spec.req.len(), args.len()).into(),
134            ));
135        }
136    }
137    Ok(())
138}
139
140pub fn exec<H: Handler>(scope: &mut Scope, handler: &mut H, code: &Code) -> Result<Val, Val> {
141    let mut i = 0;
142    let mut stack = Vec::new();
143    while i < code.len() {
144        match step(scope, handler, code, &mut i, &mut stack)? {
145            StepVal::Return(val) => return Ok(val),
146            StepVal::Yield(_) => return Err("Yielding does not make sense here".into()),
147            StepVal::None => {}
148        }
149    }
150    assert!(stack.is_empty());
151    Ok(Val::Nil)
152}
153
154enum StepVal {
155    Return(Val),
156    Yield(Val),
157    None,
158}
159
160fn step<H: Handler>(
161    scope: &mut Scope,
162    handler: &mut H,
163    code: &Code,
164    i: &mut usize,
165    stack: &mut Vec<Val>,
166) -> Result<StepVal, Val> {
167    let op = code.fetch(*i);
168    *i += 1;
169    match op {
170        Opcode::Nil => {
171            stack.push(Val::Nil);
172        }
173        Opcode::Bool(x) => {
174            stack.push(Val::Bool(*x));
175        }
176        Opcode::Number(x) => {
177            stack.push(Val::Number(*x));
178        }
179        Opcode::String(x) => {
180            stack.push(Val::String(x.clone()));
181        }
182        Opcode::MakeList(len) => {
183            let start = stack.len() - *len as usize;
184            let list: Vec<_> = stack.drain(start..).collect();
185            stack.push(list.into());
186        }
187        Opcode::NewFunc(code) => {
188            stack.push(Val::Func(Func(code.clone())));
189        }
190        Opcode::Pop => {
191            stack.pop().unwrap();
192        }
193        Opcode::Dup => {
194            let x = stack.last().unwrap().clone();
195            stack.push(x);
196        }
197        Opcode::Dup2 => {
198            let len = stack.len();
199            let a = stack[len - 2].clone();
200            let b = stack[len - 1].clone();
201            stack.push(a);
202            stack.push(b);
203        }
204        Opcode::Unpack(n) => {
205            let elements = stack.pop().unwrap();
206            if let Some(list) = elements.list() {
207                if list.borrow().len() != *n as usize {
208                    scope.push_trace(code.marks()[*i - 1].clone());
209                    return Err(Val::String(
210                        format!(
211                            "Expected {} values, but got a list with {} values",
212                            n,
213                            list.borrow().len(),
214                        )
215                        .into(),
216                    ));
217                }
218                for item in list.borrow().iter() {
219                    stack.push(item.clone());
220                }
221            } else {
222                let genobj = elements.expect_genobj()?;
223                let mut genobj = genobj.0.borrow_mut();
224                let vec = genobj.to_vec(scope, handler)?;
225                if vec.len() != *n as usize {
226                    scope.push_trace(code.marks()[*i - 1].clone());
227                    return Err(Val::String(
228                        format!("Expected {} values, but got {} values", n, vec.len(),).into(),
229                    ));
230                }
231                stack.extend(vec);
232            }
233        }
234        Opcode::Get(vscope, index) => {
235            let val = scope.get(*vscope, *index).clone();
236            if let Val::Invalid = val {
237                return Err(Val::String(
238                    format!(
239                        "Variable {} used before being set",
240                        scope.get_name(*vscope, *index)
241                    )
242                    .into(),
243                ));
244            }
245            stack.push(val);
246        }
247        Opcode::Set(vscope, index) => {
248            let val = stack.pop().unwrap();
249            scope.set(*vscope, *index, val);
250        }
251        Opcode::Tee(vscope, index) => {
252            let val = stack.last().unwrap().clone();
253            scope.set(*vscope, *index, val);
254        }
255        Opcode::Return => {
256            let val = stack.pop().unwrap();
257            return Ok(StepVal::Return(val));
258        }
259        Opcode::Yield => {
260            let val = stack.pop().unwrap();
261            return Ok(StepVal::Yield(val));
262        }
263        Opcode::Next => {
264            let genobj = stack.last().unwrap().clone();
265            let genobj = genobj.expect_genobj()?;
266            let mut genobj = genobj.0.borrow_mut();
267            match genobj.resume(scope, handler, Val::Nil)? {
268                Some(val) => {
269                    stack.push(val);
270                    stack.push(true.into());
271                }
272                None => {
273                    stack.push(Val::Nil);
274                    stack.push(false.into());
275                }
276            }
277        }
278        Opcode::CallFunc(argc) => {
279            let old_len = stack.len();
280            let new_len = old_len - (*argc as usize);
281            let args: Vec<Val> = stack.drain(new_len..).collect();
282            let func = stack.pop().unwrap();
283            let func = if let Some(func) = func.func() {
284                func
285            } else {
286                scope.push_trace(code.marks()[*i - 1].clone());
287                return Err(format!("{} is not a function", func).into());
288            };
289
290            scope.push_trace(code.marks()[*i - 1].clone());
291            if func.generator() {
292                // generator; create a generator object
293                let genobj = mkgenobj(func.clone(), args)?;
294                stack.push(genobj);
295            } else {
296                // this is a normal function, call it
297                let ret = callfunc(scope, handler, &func, args)?;
298                stack.push(ret);
299            }
300            scope.pop_trace();
301        }
302        Opcode::Binop(op) => {
303            let rhs = stack.pop().unwrap();
304            let lhs = stack.pop().unwrap();
305            let ret = match op {
306                // arithmetic operators
307                Binop::Arithmetic(aop) => {
308                    let lhs = if let Some(lhs) = lhs.number() {
309                        lhs
310                    } else {
311                        scope.push_trace(code.marks()[*i - 1].clone());
312                        return Err(format!(
313                            concat!(
314                                "The left hand side of this arithmetic operation ",
315                                "should be a number but got {:?}"
316                            ),
317                            lhs
318                        )
319                        .into());
320                    };
321                    let rhs = if let Some(rhs) = rhs.number() {
322                        rhs
323                    } else {
324                        scope.push_trace(code.marks()[*i - 1].clone());
325                        return Err(format!(
326                            concat!(
327                                "The right hand side of this arithmetic operation ",
328                                "should be a number but got {:?}"
329                            ),
330                            rhs
331                        )
332                        .into());
333                    };
334                    match aop {
335                        ArithmeticBinop::Add => Val::Number(lhs + rhs),
336                        ArithmeticBinop::Subtract => Val::Number(lhs - rhs),
337                        ArithmeticBinop::Multiply => Val::Number(lhs * rhs),
338                        ArithmeticBinop::Divide => Val::Number(lhs / rhs),
339                        ArithmeticBinop::TruncDivide => Val::Number((lhs / rhs).trunc()),
340                        ArithmeticBinop::Remainder => Val::Number(lhs % rhs),
341                    }
342                }
343
344                // comparison operators
345                Binop::Equal => Val::Bool(lhs == rhs),
346                Binop::NotEqual => Val::Bool(lhs != rhs),
347                Binop::LessThan => Val::Bool(lhs.lt(&rhs)?),
348                Binop::LessThanOrEqual => Val::Bool(!rhs.lt(&lhs)?),
349                Binop::GreaterThan => Val::Bool(rhs.lt(&lhs)?),
350                Binop::GreaterThanOrEqual => Val::Bool(!lhs.lt(&rhs)?),
351
352                // list
353                Binop::Append => {
354                    let list = lhs.expect_list()?;
355                    list.borrow_mut().push(rhs);
356                    lhs
357                }
358            };
359            stack.push(ret);
360        }
361        Opcode::Unop(op) => {
362            let val = stack.pop().unwrap();
363            let ret = match op {
364                Unop::Arithmetic(aop) => {
365                    let val = if let Some(val) = val.number() {
366                        val
367                    } else {
368                        scope.push_trace(code.marks()[*i - 1].clone());
369                        return Err(format!(
370                            concat!(
371                                "The argument to this unary arithmetic operator ",
372                                "should be a number but got {:?}"
373                            ),
374                            val
375                        )
376                        .into());
377                    };
378                    match aop {
379                        ArithmeticUnop::Negative => Val::Number(-val),
380                        ArithmeticUnop::Positive => Val::Number(val),
381                    }
382                }
383                Unop::Len => match val {
384                    Val::String(s) => Val::Number(s.charlen() as f64),
385                    Val::List(list) => Val::Number(list.borrow().len() as f64),
386                    _ => {
387                        scope.push_trace(code.marks()[*i - 1].clone());
388                        return Err(format!(
389                            concat!("LEN requires a string or list argument but got {}"),
390                            val,
391                        )
392                        .into());
393                    }
394                },
395            };
396            stack.push(ret);
397        }
398        Opcode::Print => {
399            let x = stack.pop().unwrap();
400            handler.print(scope, x)?;
401        }
402        Opcode::Disasm => {
403            let f = stack.pop().unwrap();
404            if let Val::Func(func) = &f {
405                stack.push(func.0.format().into());
406            } else {
407                scope.push_trace(code.marks()[*i - 1].clone());
408                return Err(
409                    format!(concat!("DISASM requires a function argument but got {}"), f,).into(),
410                );
411            }
412        }
413        Opcode::AddToTest => {
414            let val = stack.last().unwrap().clone();
415            if let Val::Func(code) = &val {
416                scope.tests.push(code.0.clone());
417            } else {
418                scope.push_trace(code.marks()[*i - 1].clone());
419                return Err(format!(
420                    concat!("Tests need to be functions, but {} is not a function"),
421                    val,
422                )
423                .into());
424            }
425        }
426        Opcode::Assert => {
427            let val = stack.pop().unwrap();
428            if !val.truthy() {
429                scope.push_trace(code.marks()[*i - 1].clone());
430                return Err(format!(concat!("Assertion failed"),).into());
431            }
432        }
433        Opcode::AssertEq => {
434            let rhs = stack.pop().unwrap();
435            let lhs = stack.pop().unwrap();
436            if lhs != rhs {
437                scope.push_trace(code.marks()[*i - 1].clone());
438                return Err(format!(
439                    concat!("Assertion failed: expected {} to equal {}"),
440                    lhs, rhs,
441                )
442                .into());
443            }
444        }
445        Opcode::Goto(pos) => {
446            *i = *pos as usize;
447        }
448        Opcode::GotoIfFalse(pos) => {
449            let item = stack.pop().unwrap();
450            if !item.truthy() {
451                *i = *pos as usize;
452            }
453        }
454        Opcode::GotoIfFalseNoPop(pos) => {
455            let item = stack.last().unwrap();
456            if !item.truthy() {
457                *i = *pos as usize;
458            }
459        }
460        Opcode::Label(_)
461        | Opcode::UnresolvedGoto(_)
462        | Opcode::UnresolvedGotoIfFalse(_)
463        | Opcode::UnresolvedGotoIfFalseNoPop(_) => {
464            panic!("Unresolved opcode: {:?}", op);
465        }
466    }
467    Ok(StepVal::None)
468}
469
470#[derive(Debug, Clone, Copy)]
471pub enum VarScope {
472    Local,
473    Global,
474}
475
476pub struct Scope {
477    globals: IndexedMap,
478    locals: Vec<IndexedMap>,
479    trace: Vec<Mark>,
480    tests: Vec<Rc<Code>>,
481}
482
483impl Scope {
484    pub fn new() -> Self {
485        Self {
486            globals: IndexedMap::new(),
487            locals: vec![],
488            trace: vec![],
489            tests: vec![],
490        }
491    }
492    pub fn get_name(&self, vscope: VarScope, index: u32) -> &RcStr {
493        match vscope {
494            VarScope::Global => self.globals.get_key(index).unwrap(),
495            VarScope::Local => self.locals.last().unwrap().get_key(index).unwrap(),
496        }
497    }
498    pub fn get(&self, vscope: VarScope, index: u32) -> &Val {
499        match vscope {
500            VarScope::Global => &self.globals.values[index as usize],
501            VarScope::Local => &self.locals.last().unwrap().values[index as usize],
502        }
503    }
504    pub fn set(&mut self, vscope: VarScope, index: u32, val: Val) {
505        match vscope {
506            VarScope::Global => self.globals.values[index as usize] = val,
507            VarScope::Local => self.locals.last_mut().unwrap().values[index as usize] = val,
508        }
509    }
510    pub fn push(&mut self, locals: &Vec<Var>) {
511        self.locals.push(new_locals_from_vars(locals));
512    }
513    pub fn push_existing_locals(&mut self, map: IndexedMap) {
514        self.locals.push(map);
515    }
516    pub fn pop(&mut self) -> IndexedMap {
517        self.locals.pop().unwrap()
518    }
519    pub fn push_trace(&mut self, mark: Mark) {
520        self.trace.push(mark);
521    }
522    pub fn pop_trace(&mut self) {
523        self.trace.pop();
524    }
525}
526
527fn new_locals_from_vars(vars: &Vec<Var>) -> IndexedMap {
528    let mut map = IndexedMap::new();
529    for var in vars {
530        let index = map.insert(var.name.clone(), Val::Nil);
531        assert_eq!(index, var.index);
532    }
533    map
534}
535
536pub struct IndexedMap {
537    values: Vec<Val>,
538    map: HashMap<RcStr, u32>,
539}
540
541impl IndexedMap {
542    pub fn new() -> Self {
543        Self {
544            values: vec![],
545            map: HashMap::new(),
546        }
547    }
548    pub fn insert(&mut self, key: RcStr, val: Val) -> u32 {
549        let i = self.values.len() as u32;
550        self.values.push(val);
551        self.map.insert(key, i);
552        i
553    }
554    pub fn set_by_index(&mut self, i: u32, val: Val) {
555        self.values[i as usize] = val;
556    }
557    pub fn get_by_key(&self, key: &RcStr) -> Option<&Val> {
558        self.map.get(key).map(|i| &self.values[*i as usize])
559    }
560    pub fn get_by_index(&self, i: u32) -> Option<&Val> {
561        self.values.get(i as usize)
562    }
563    pub fn get_key(&self, index: u32) -> Option<&RcStr> {
564        for (key, i) in &self.map {
565            if *i == index {
566                return Some(key);
567            }
568        }
569        None
570    }
571    pub fn len(&self) -> usize {
572        self.values.len()
573    }
574}
575
576/// generator object
577/// created by calling generator functions
578pub struct GenObj {
579    code: Rc<Code>,
580    locals: IndexedMap,
581    i: usize,
582    stack: Vec<Val>,
583}
584
585impl GenObj {
586    pub fn resume<H: Handler>(
587        &mut self,
588        scope: &mut Scope,
589        handler: &mut H,
590        val: Val,
591    ) -> Result<Option<Val>, Val> {
592        if self.i >= self.code.len() {
593            return Ok(None);
594        }
595        scope.push_existing_locals(std::mem::replace(&mut self.locals, IndexedMap::new()));
596        self.stack.push(val);
597        let result = self.loop_(scope, handler);
598        self.locals = scope.pop();
599        result
600    }
601    pub fn to_vec<H: Handler>(
602        &mut self,
603        scope: &mut Scope,
604        handler: &mut H,
605    ) -> Result<Vec<Val>, Val> {
606        let mut ret = Vec::new();
607        while let Some(val) = self.resume(scope, handler, Val::Nil)? {
608            ret.push(val);
609        }
610        Ok(ret)
611    }
612    fn loop_<H: Handler>(
613        &mut self,
614        scope: &mut Scope,
615        handler: &mut H,
616    ) -> Result<Option<Val>, Val> {
617        while self.i < self.code.len() {
618            match step(scope, handler, &self.code, &mut self.i, &mut self.stack)? {
619                StepVal::Return(_) => {
620                    self.i = self.code.len();
621                    self.clear(); // release all local vars etc
622                    return Ok(None);
623                }
624                StepVal::Yield(val) => return Ok(Some(val)),
625                StepVal::None => {}
626            }
627        }
628        self.clear();
629        Ok(None)
630    }
631    fn clear(&mut self) {
632        self.stack = vec![];
633        self.locals = IndexedMap::new();
634    }
635}