use crate::nuke::{NkSum, NkRef};
use crate::nkgc::{PV, SymID, Cons};
use crate::r8vm::{R8VM, r8c, ArgSpec, SYSTEM_FUNCTIONS};
use crate::r8vm::r8c::Op as R8C;
use crate::chasm::{ChOp, ChASM, ChASMOpName, Lbl};
use crate::chasm;
use crate::r8vm::mk_default_sysfn_idx_lookup;
use crate::error::{Error, ErrorKind, Source};
use crate::ast::{Value, ValueKind, ListBuilder, Arith2, Cmp2};
use crate::ast;
use crate::r8vm::r8c::OpName::*;
use std::collections::HashMap;
use fnv::{FnvHashMap, FnvHashSet};
use std::hash::Hash;
use std::mem;
use std::sync::Mutex;
use std::sync::atomic::{AtomicUsize, Ordering};
static LAMBDA_COUNT: AtomicUsize = AtomicUsize::new(0);
lazy_static! {
static ref LABEL_CNT: Mutex<HashMap<String, usize>> =
Mutex::new(HashMap::new());
}
type VarIdx = u32;
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
pub enum BoundVar {
Local(VarIdx),
Env(VarIdx),
}
#[derive(Default, Debug)]
pub struct Env {
vars: Vec<SymID>,
statics: FnvHashMap<SymID, usize>,
}
type EnvMap = FnvHashMap<SymID, BoundVar>;
impl Env {
pub fn new(args: Vec<SymID>) -> Env {
let mut env = Env {
vars: args,
statics: FnvHashMap::default()
};
env.defvar(Builtin::IP.sym());
env.defvar(Builtin::Frame.sym());
env
}
pub fn len(&self) -> usize {
self.vars.len()
}
pub fn empty() -> Env {
Env::new(vec![])
}
pub fn none() -> Env {
Env { vars: vec![], statics: FnvHashMap::default() }
}
pub fn defvar(&mut self, var: SymID) {
self.vars.push(var);
}
pub fn anon(&mut self) -> usize {
let pos = self.vars.len();
self.vars.push(Builtin::Epsilon.sym());
pos
}
pub fn defenv(&mut self, var: SymID, env_idx: usize) {
self.statics.insert(var, env_idx);
}
pub fn pop(&mut self, n: usize) {
let new_top = self.vars.len() - n;
self.vars.truncate(new_top);
}
pub fn get_env_idx(&self, var: SymID) -> Option<usize> {
self.statics.get(&var).copied()
}
pub fn get_idx(&mut self, var: SymID) -> Option<VarIdx> {
for (i, &v) in self.vars.iter().enumerate().rev() {
if var == v {
return Some(i as VarIdx);
}
}
None
}
pub fn iter_statics(&self) -> impl Iterator<Item = (&SymID, &usize)> {
self.statics.iter()
}
pub fn as_map(&self) -> FnvHashMap<SymID, BoundVar> {
let mut map = FnvHashMap::default();
for (&v, &i) in self.iter_statics() {
map.insert(v, BoundVar::Env(i as VarIdx));
}
for (i, &v) in self.vars.iter().enumerate() {
map.insert(v, BoundVar::Local(i as VarIdx));
}
map
}
}
#[allow(unused)]
enum Cond {
EqZero(SymID),
NeqZero(SymID),
When(PV),
Unless(PV),
}
macro_rules! builtins {
($(($sym:ident, $str:expr)),*) => {
#[repr(i32)]
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum Builtin {
$(
$sym
),*
}
impl Builtin {
pub fn from_sym(n: SymID) -> Option<Builtin> {
let nint = i32::from(n);
if nint >= 0 && nint < count_args!($($sym),*) {
Some(unsafe { mem::transmute(n) })
} else {
None
}
}
pub const fn num_builtins() -> usize {
count_args!($($sym),*)
}
pub fn get_str(&self) -> &'static str {
let idx: i32 = unsafe { mem::transmute(*self) };
BUILTIN_SYMBOLS[idx as usize]
}
pub fn sym(&self) -> SymID {
let id: i32 = unsafe { mem::transmute(*self) };
SymID { id }
}
pub fn to_string(&self) -> String {
String::from(self.get_str())
}
}
impl std::fmt::Display for Builtin {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
write!(f, "{}", match self { $(Builtin::$sym => $str),* })
}
}
pub const BUILTIN_SYMBOLS: [&'static str; count_args!($($sym),*)] = [
$($str),*
];
}
}
builtins! {
(Unknown, "?"),
(If, "if"),
(Compile, "compile"),
(Let, "let"),
(And, "and"),
(Or, "or"),
(Set, "set"),
(Quote, "quote"),
(Quasi, "sys::quasi"),
(Unquote, "sys::unquote"),
(USplice, "sys::usplice"),
(Loop, "loop"),
(Cdr, "cdr"),
(Car, "car"),
(Cons, "cons"),
(List, "list"),
(Append, "append"),
(Vector, "vec"),
(Get, "get"),
(Push, "push"),
(Pop, "pop"),
(Len, "len"),
(Lambda, "lambda"),
(Call, "call"),
(Apply, "apply"), (True, "true"),
(False, "false"),
(Add, "+"),
(Sub, "-"),
(Div, "/"),
(Mul, "*"),
(Gt, ">"),
(Lt, "<"),
(Lte, "<="),
(Gte, ">="),
(Eq, "="),
(Not, "not"),
(DefineStatic, "intr::define-static"),
(Define, "define"),
(Progn, "progn"),
(ArgOptional, "&?"),
(ArgBody, "&body"),
(ArgRest, "&rest"),
(EvalWhen, "eval-when"),
(KwCompile, ":compile"),
(KwLoad, ":load"),
(KwExec, ":exec"),
(KwLocal, ":local"),
(KwEnv, ":env"),
(KwPass, ":pass"),
(KwFail, ":fail"),
(KwOk, ":ok"),
(LispOk, "ok"),
(Fail, "fail"),
(Symbol, "symbol"),
(Integer, "integer"),
(String, "string"),
(Closure, "closure"),
(Stream, "stream"),
(Deref, "sys::deref"),
(Ref, "ref"),
(New, "new"),
(Next, "next"),
(Break, "break"),
(Number, "number"),
(UnsignedInteger, "unsigned-integer"),
(Float, "float"),
(Bool, "bool"),
(HaltFunc, "<ζ>::halt"),
(IP, "<ζ>::ip"),
(Frame, "<ζ>::frame"),
(DebugVarIdx, "dbg::var-idx"),
(LambdaObject, "<ζ>::lambda-object"),
(Subr, "subr"),
(Nil, "nil"),
(Epsilon, "")
}
pub type LblLookup = FnvHashMap<u32, Lbl>;
pub type Linked = (Vec<R8C>,
LblLookup,
Vec<NkSum>);
pub fn arg_parse(args: &Value) -> Result<(Vec<SymID>, ArgSpec), Error> {
let mut syms = Vec::new();
let mut spec = ArgSpec::default();
let mut it = args.iter();
let mut modifier = None;
let mut had_opt = false;
while let Some(arg) = it.next() {
if let Value { kind: ValueKind::Symbol(sym), .. } = arg {
use Builtin::*;
match Builtin::from_sym(*sym) {
Some(x @ ArgOptional) => {
modifier = Some(x);
had_opt = true;
}
Some(x @ ArgRest) => modifier = Some(x),
Some(ArgBody) => modifier = Some(ArgRest),
_ => {
match modifier.take() {
Some(ArgOptional) => spec.nopt += 1,
Some(ArgRest) => {
spec.rest = true;
syms.push(*sym);
break;
}
None if had_opt => return Err(ErrorKind::SyntaxError {
msg: "Normal argument follows &?".to_string()
}.into()),
None | Some(_) => spec.nargs += 1,
}
syms.push(*sym);
}
}
} else {
return Err(ErrorKind::SyntaxError {
msg: format!("Did not expect: {}", arg),
}.into());
}
}
if it.next().is_some() {
return Err(ErrorKind::SyntaxError {
msg: "Additional argument follows &rest".to_string(),
}.into());
}
Ok((syms, spec))
}
fn quasi_transform(root: Value) -> Result<Value, Error> {
enum Elem {
MaybeQuoted(Value),
Unquoted(Value)
}
use Builtin::*;
use Elem::*;
if root.is_atom() {
return Ok(lisp_qq!(Quote ,root))
}
let root_src = root.src.clone();
let mut lisb = ListBuilder::new();
lisb.push_sym(Append.sym(), root.src.clone());
let mut li = Vec::new();
macro_rules! flush {() => {{
if !li.is_empty() {
let mut sub_lisb = ListBuilder::new();
sub_lisb.push_sym(List.sym(), root_src.clone());
for arg in li.drain(..) {
match arg {
MaybeQuoted(arg) if arg.is_atom() => {
let mut lisb = ListBuilder::new();
lisb.push_sym(Quote.sym(), arg.src.clone());
lisb.push(arg);
sub_lisb.push(lisb.get());
}
Unquoted(arg) | MaybeQuoted(arg) => sub_lisb.push(arg),
}
}
lisb.push(sub_lisb.get());
}
}}}
for arg in root.into_iter() {
if let Some(bt) = arg.op().and_then(Builtin::from_sym) {
match bt {
Unquote => li.push(Unquoted(raise_inner(bt.sym(), arg)?)),
USplice => {
flush!();
lisb.push(raise_inner(bt.sym(), arg)?);
},
Quasi => {
let inner = raise_inner(bt.sym(), arg)?;
li.push(MaybeQuoted(
lisp_qq!(Quote ,(quasi_transform(inner)?))));
},
_ => li.push(MaybeQuoted(quasi_transform(arg)?)),
}
} else {
li.push(MaybeQuoted(quasi_transform(arg)?));
}
}
flush!();
Ok(lisb.get())
}
fn raise_inner(op: SymID, v: Value) -> Result<Value, Error> {
let src = v.src.clone();
let nargs = v.nargs();
match v.into_args().next() {
Some(arg) if nargs == 1 => Ok(arg),
_ => err_src!(src, ArgError,
expect: ArgSpec::normal(1),
got_num: nargs as u32,
op)
}
}
type VarSet = FnvHashSet<(SymID, BoundVar)>;
struct ClzScoper<'a> {
lowered: VarSet,
env: Env,
outside: &'a EnvMap,
}
impl ClzScoper<'_> {
pub fn new(args: Vec<SymID>, outside: &EnvMap) -> ClzScoper {
ClzScoper {
lowered: FnvHashSet::default(),
env: Env::new(args),
outside
}
}
fn find_quasi(&mut self, ast: &Value) -> Result<(), Error> {
match ast.bt_op() {
Some(Builtin::Unquote) => for arg in ast.args() {
self.find(arg)?;
},
_ => for arg in ast.args() {
self.find_quasi(arg)?;
}
}
Ok(())
}
pub fn handle_app<'z>(&mut self,
op: SymID,
args: impl Iterator<Item = &'z Value>
) -> Result<(), Error> {
if let Some(&var) = self.outside.get(&op) {
self.lowered.insert((op, var));
}
for arg in args {
self.find(arg)?;
}
Ok(())
}
pub fn find(&mut self, ast: &Value) -> Result<(), Error> {
if let Some(bt) = ast.bt_op() {
match bt {
Builtin::Quasi => for arg in ast.args() {
self.find_quasi(arg)?
},
Builtin::Quote => (),
Builtin::Let => {
let ast::Let(pairs, rest) = ast.bt_let()?;
let len = pairs.len();
for ast::LetBinding(name, ..) in pairs {
self.env.defvar(name);
}
self.find(rest)?;
self.env.pop(len);
}
Builtin::Lambda => {
let ast::Lambda(ast::ArgList(_, args), body) =
ast.bt_lambda()?;
for arg in args {
self.env.defvar(arg);
}
self.find(body)?;
}
_ => self.handle_app(bt.sym(), ast.args())?,
}
} else if let Some(op) = ast.op() {
self.handle_app(op, ast.args())?;
} else if ast.is_atom() {
if let ValueKind::Symbol(var) = ast.kind {
if self.env.get_idx(var).is_some() {
} else if let Some(&bound) = self.outside.get(&var) {
self.lowered.insert((var, bound));
} else if var == Builtin::Nil.sym() {
} else {
return err_src!(ast.src.clone(), UndefinedVariable, var);
}
}
} else {
for part in ast.iter() {
self.find(part)?;
}
}
Ok(())
}
pub fn scope_body(mut self, body: &Value) -> Result<VarSet, Error> {
self.find(body)?;
Ok(self.lowered)
}
}
pub unsafe fn pv_to_value(v: PV, src: &Source) -> Value {
let kind = match v {
PV::Sym(sym) => ValueKind::Symbol(sym),
PV::Nil => ValueKind::Nil,
PV::Int(x) => ValueKind::Int(x),
PV::Bool(x) => ValueKind::Bool(x),
PV::Real(x) => ValueKind::Real(x),
PV::Ref(p) => match (*p).match_ref() {
NkRef::Cons(Cons { car, cdr }) =>
ValueKind::Cons(Box::new(pv_to_value(*car, src)),
Box::new(pv_to_value(*cdr, src))),
NkRef::String(s) => ValueKind::String(s.clone()),
NkRef::PV(v) => pv_to_value(*v, src).kind,
x => unimplemented!("{:?}", x),
}
x => unimplemented!("{:?}", x)
};
Value { kind, src: src.clone() }
}
pub struct R8Compiler<'a, 'b> {
asm: ChASM,
pub(crate) estack: Vec<Env>,
loops: Vec<LoopCtx>,
consts: Vec<NkSum>,
symtbl: HashMap<SymID, isize>,
vm: &'a mut R8VM<'b>,
}
#[derive(Clone, Copy)]
struct LoopCtx {
start: Lbl,
end: Lbl,
ret: bool,
height: usize,
}
lazy_static! {
static ref SYSFNS: HashMap<&'static str, i64> =
mk_default_sysfn_idx_lookup();
}
impl<'a, 'b> R8Compiler<'a, 'b> {
pub fn new(vm: &'a mut R8VM<'b>) -> R8Compiler<'a, 'b> {
R8Compiler {
asm: Default::default(),
estack: Default::default(),
consts: Default::default(),
symtbl: Default::default(),
loops: Default::default(),
vm
}
}
pub fn compile_top(&mut self,
ret: bool,
code: &Value) -> Result<(), Error> {
self.estack.push(Env::empty());
self.compile_seq(ret, code.iter())
}
pub fn globals(&self) -> Option<impl Iterator<Item = (&SymID, &usize)>> {
self.estack.first().map(|s| s.iter_statics())
}
fn compile_seq<'z>(&mut self,
ret: bool,
seq: impl Iterator<Item = &'z Value>
) -> Result<(), Error> {
let mut args: Vec<_> = seq.collect();
let last = args.pop();
for arg in args.iter() {
self.compile(false, arg)?;
}
if let Some(v) = last {
self.compile(ret, v)?;
} else if ret {
self.asm.op(chasm!(NIL));
}
Ok(())
}
pub fn enter_fn(&mut self,
args: Vec<SymID>,
spec: ArgSpec) -> Result<(), Error> {
let mut env = Env::none();
if spec.has_env() {
env.defvar(Builtin::LambdaObject.sym());
}
for arg in args.iter() {
env.defvar(*arg);
}
env.defvar(Builtin::IP.sym());
env.defvar(Builtin::Frame.sym());
self.estack.push(env);
if spec.is_special() {
self.asm.op(chasm!(SAV 2));
if spec.has_opt() {
self.asm.op(chasm!(TOP spec.nargs));
self.asm.op(chasm!(JV 1, spec.nopt));
for _ in 0..spec.nopt {
self.asm.op(chasm!(NIL));
}
}
if spec.has_body() {
self.asm.op(chasm!(TOP spec.nargs + spec.nopt));
self.asm.op(chasm!(VLIST));
}
if spec.has_env() {
self.asm.op(chasm!(CLZEXP));
}
self.asm.op(chasm!(RST));
}
Ok(())
}
pub fn compile_fn(&mut self, _name: SymID, args: &Value, body: &Value) -> Result<ArgSpec, Error> {
let (args, spec) = arg_parse(args)?;
self.enter_fn(args, spec)?;
self.compile_seq(true, body.iter())?;
self.leave_fn();
Ok(spec)
}
fn leave_fn(&mut self) {
self.asm.op(chasm!(RET));
self.estack.pop();
}
pub fn argument_clinic(mut code: &Value) -> Result<(bool, &Value), Error> {
let mut flipped = false;
while let Some(op) = code.op() {
if Builtin::from_sym(op) != Some(Builtin::Not) {
break;
}
let args: Vec<_> = code.args().collect();
match &args[..] {
[head] => code = head,
_ => return err_src!(code.src.clone(),
ArgError,
expect: ArgSpec::normal(1),
got_num: args.len() as u32,
op),
}
flipped = !flipped;
}
Ok((flipped, code))
}
fn bt_if(&mut self, ret: bool, code: &Value) -> Result<(), Error> {
let args: Vec<_> = code.args().collect();
let (cond, if_true, if_false) = match &args[..] {
[cond, if_true, if_false] => (cond, if_true, Some(if_false)),
[cond, if_true] => (cond, if_true, None),
_ => return err_src!(code.src.clone(),
ArgError,
expect: ArgSpec::opt(2, 1),
got_num: args.len() as u32,
op: Builtin::If.sym())
};
let (flipped, cond) = R8Compiler::argument_clinic(cond)?;
use ValueKind::*;
let ((jt, jn), cond) = match cond.bt_cmp2() {
Some(Cmp2::Eq(Value { kind: Int(0), .. }, v)) |
Some(Cmp2::Eq(v, Value { kind: Int(0), ..})) => ((JZ, JNZ), v),
_ => ((JT, JN), cond)
};
self.compile(true, cond)?;
let end_lbl = self.asm.label("if_end");
let if_false_lbl = self.asm.label("if_false");
let _jmp_loc = if flipped {
self.asm.op(chasm!(jt if_false_lbl))
} else {
self.asm.op(chasm!(jn if_false_lbl))
};
self.compile(ret, if_true)?;
if let Some(if_false) = if_false {
self.asm_op(chasm!(JMP end_lbl));
self.asm.mark(if_false_lbl);
self.compile(ret, if_false)?;
self.asm.mark(end_lbl);
} else if ret {
self.asm_op(chasm!(JMP end_lbl));
self.asm.mark(if_false_lbl);
self.asm.op(chasm!(NIL));
self.asm.mark(end_lbl);
} else {
self.asm.mark(if_false_lbl);
}
Ok(())
}
fn bt_and(&mut self, ret: bool, code: &Value) -> Result<(), Error> {
let mut it = code.args().peekable();
if it.peek().is_none() {
self.asm.op(chasm!(BOOL 1));
return Ok(());
}
let end_l = self.asm.label("and_end");
let and_exit = self.asm.label("and_early_exit");
while let Some(part) = it.next() {
let (flip, part) = R8Compiler::argument_clinic(part)?;
self.compile(it.peek().is_some() || ret, part)?;
if flip {
self.asm.op(chasm!(JT and_exit));
} else {
self.asm.op(chasm!(JN and_exit));
}
}
self.asm.pop();
if ret {
self.asm.op(chasm!(JMP end_l));
self.asm.mark(and_exit);
self.asm.op(chasm!(NIL));
self.asm.mark(end_l);
} else {
self.asm.mark(and_exit);
}
Ok(())
}
fn bt_or(&mut self, ret: bool, code: &Value) -> Result<(), Error> {
let mut it = code.args().peekable();
if it.peek().is_none() {
self.asm.op(chasm!(BOOL 0));
return Ok(());
}
let end_l = self.asm.label("or_end");
for part in code.args() {
let (flip, part) = R8Compiler::argument_clinic(part)?;
self.compile(it.peek().is_some() || ret, part)?;
if ret { self.asm.op(chasm!(DUP)); }
if flip { self.asm.op(chasm!(JN end_l)); }
else { self.asm.op(chasm!(JT end_l)); }
if ret { self.asm.op(chasm!(POP 1)); }
}
if ret { self.asm.op(chasm!(NIL)); }
self.asm.mark(end_l);
Ok(())
}
fn bt_not(&mut self, code: &Value) -> Result<(), Error> {
let (flip, code) = R8Compiler::argument_clinic(code)?;
self.compile(true, code)?;
if flip {
self.asm.op(chasm!(NOT));
}
Ok(())
}
fn with_env<T>(&mut self, f: impl Fn(&mut Env) -> T) -> Result<T, Error> {
self.estack
.last_mut()
.map(f)
.ok_or_else(|| "No environment".into())
}
fn bt_let(&mut self, ret: bool, code: &Value) -> Result<(), Error> {
let ast::Let(pairs, rest) = code.bt_let()?;
let len = pairs.len();
for ast::LetBinding(name, val, ..) in pairs {
self.compile(true, val)?;
self.with_env(|env| env.defvar(name))?;
}
self.compile_seq(ret, rest.iter())?;
if ret {
self.asm.op(chasm!(POPA 1, len));
} else {
self.asm_op(chasm!(POP len));
}
self.env_pop(len)
}
fn bt_define(&mut self, ret: bool, code: &Value) -> Result<(), Error> {
match code.bt_define()? {
ast::Define::Var(name, init) =>
self.define_static(ret, name, init)
.map_err(|op| op.op(Builtin::Define.sym())),
ast::Define::Func(name, ast::ArgList(spec, args), code) => {
let mut cc = R8Compiler::new(self.vm);
cc.enter_fn(args, spec)?;
cc.compile_seq(true, code.iter())?;
cc.leave_fn();
let res = cc.link()?;
self.vm.add_func(name, res, spec);
if ret {
self.asm_op(chasm!(CLZ name.id, 0));
}
Ok(())
}
}
}
fn bt_loop<'z>(&mut self,
ret: bool,
seq: impl Iterator<Item = &'z Value>) -> Result<(), Error> {
let start = self.asm.label("loop_start");
let end = self.asm.label("loop_end");
let height = self.with_env(|env| env.len())?;
self.loops.push(LoopCtx { start, end, ret, height });
self.asm.mark(start);
self.compile_seq(false, seq)?;
self.asm.op(chasm!(JMP start));
self.asm.mark(end);
self.loops.pop();
Ok(())
}
#[inline]
fn asm_op(&mut self, op: ChOp) {
self.asm.op(op);
}
fn bt_next(&mut self, code: &Value) -> Result<(), Error> {
let outer = self.loops
.last()
.copied()
.ok_or(error_src!(code.src.clone(), OutsideContext,
op: Builtin::Next.sym(),
ctx: Builtin::Loop.sym()))?;
ArgSpec::none().check(Builtin::Next.sym(), code.nargs())?;
let LoopCtx { start, height, .. } = outer;
let dist = self.with_env(|env| env.len())? - height;
self.asm_op(chasm!(POP dist));
self.asm_op(chasm!(JMP start));
Ok(())
}
fn bt_break(&mut self, code: &Value) -> Result<(), Error> {
let outer = self.loops
.last()
.copied()
.ok_or(error_src!(code.src.clone(), OutsideContext,
op: Builtin::Break.sym(),
ctx: Builtin::Loop.sym()))?;
ArgSpec::opt(0, 1).check(Builtin::Break.sym(), code.nargs())?;
let args: Vec<_> = code.args().collect();
let LoopCtx { end, ret, height, .. } = outer;
let dist = self.with_env(|env| env.len())? - height;
let popa = |cc: &mut R8Compiler| if dist > 0 {
cc.asm_op(chasm!(POPA 1, dist-1))
};
match &args[..] {
[code] if ret => {
self.compile(true, code)?;
popa(self);
}
[] if ret => {
self.asm_op(chasm!(NIL));
popa(self);
}
_ if dist > 0 => self.asm_op(chasm!(POP dist)),
_ => ()
}
self.asm_op(chasm!(JMP end));
Ok(())
}
#[inline]
fn push(&mut self, code: &Value) -> Result<usize, Error> {
self.compile(true, code)?;
self.with_env(|env| env.anon())
}
fn pushit<'z>(&mut self, args: impl Iterator<Item = &'z Value>) -> Result<usize, Error> {
let mut nargs = 0;
for arg in args {
nargs += 1;
self.push(arg)?;
}
Ok(nargs)
}
fn env_pop(&mut self, n: usize) -> Result<(), Error> {
self.with_env(|env| env.pop(n))
}
fn gen_call<'z>(&mut self,
ret: bool,
name: SymID,
op: (r8c::OpName, &[chasm::Arg]),
spec: ArgSpec,
args: impl Iterator<Item = &'z Value>) -> Result<(), Error>
{
let nargs = self.pushit(args)?;
spec.check(name, nargs as u16)?;
self.asm.add(op.0, op.1);
self.env_pop(nargs)?;
if !ret {
self.asm_op(chasm!(POP 1));
}
Ok(())
}
fn gen_call_nargs<'z>(&mut self,
ret: bool,
op: (r8c::OpName, &mut [chasm::Arg]),
nargs_idx: usize,
args: impl Iterator<Item = &'z Value>) -> Result<(), Error> {
let nargs = self.pushit(args)?;
op.1[nargs_idx] = nargs.into();
self.asm.add(op.0, op.1);
self.env_pop(nargs)?;
if !ret {
self.asm_op(chasm!(POP 1));
}
Ok(())
}
fn binop(&mut self,
code: &Value,
bop: (r8c::OpName, &[chasm::Arg])) -> Result<(), Error>
{
let mut args = code.args().peekable();
let op = code.op().unwrap();
if let Some(fst) = args.next() {
if args.peek().is_none() {
match bop.0 {
r8c::OpName::DIV => { self.asm.op(chasm!(PUSH 1)); },
r8c::OpName::SUB => { self.asm.op(chasm!(PUSH 0)); },
r8c::OpName::MUL | r8c::OpName::ADD =>
return self.compile(true, fst),
_ =>
return err_src!(code.src.clone(),
ArgError,
expect: ArgSpec::rest(2, 0),
op,
got_num: 1)
}
self.with_env(|env| env.anon())?;
self.compile(true, fst)?;
self.asm.add(bop.0, bop.1);
} else {
self.push(fst)?;
for arg in args {
self.compile(true, arg)?;
self.asm.add(bop.0, bop.1);
}
}
self.env_pop(1)?;
} else {
match bop.0 {
r8c::OpName::ADD => { self.asm.op(chasm!(PUSH 0)); },
r8c::OpName::MUL => { self.asm.op(chasm!(PUSH 1)); },
_ => return err_src!(code.src.clone(),
ArgError,
expect: ArgSpec::rest(1, 0),
op,
got_num: 0)
}
}
Ok(())
}
fn cmp_binop(&mut self,
code: &Value,
op: (r8c::OpName, &[chasm::Arg])) -> Result<(), Error>
{
let args: Vec<_> = code.args().collect();
ArgSpec::rest(2, 0).check(code.op().unwrap(),
args.len() as u16)?;
if args.len() == 2 {
return self.binop(code, op);
}
let end_l = self.asm.label("cmp_end");
let cmp_exit = self.asm.label("cmp_early_exit");
let mut it = args.windows(2);
while let Some(&[u, v]) = it.next() {
self.compile(true, u)?;
self.compile(true, v)?;
self.asm.add(op.0, op.1);
self.asm_op(chasm!(JN cmp_exit));
}
self.asm.pop();
self.asm.op(chasm!(JMP end_l));
self.asm.mark(cmp_exit);
self.asm.op(chasm!(BOOL 0));
self.asm.mark(end_l);
Ok(())
}
fn get_var_idx(&mut self,
var: SymID,
src: &Source) -> Result<BoundVar, Error> {
if let Some(idx) = self.with_env(|env| env.get_idx(var))? {
return Ok(BoundVar::Local(idx as u32));
}
for env in self.estack.iter().rev() {
if let Some(idx) = env.get_env_idx(var) {
return Ok(BoundVar::Env(idx as u32))
}
}
if let Some(idx) = self.vm.get_env_global(var) {
return Ok(BoundVar::Env(idx as u32))
}
err_src!(src.clone(), UndefinedVariable, var)
}
fn asm_set_var_idx(&mut self, idx: &BoundVar) -> Result<(), Error> {
match idx {
BoundVar::Local(idx) => self.asm.op(chasm!(STR *idx)),
BoundVar::Env(idx) => self.asm.op(chasm!(SET *idx)),
};
Ok(())
}
fn asm_get_var(&mut self,
var: SymID,
src: &Source) -> Result<(), Error> {
match self.get_var_idx(var, src)? {
BoundVar::Local(idx) => self.asm.op(chasm!(MOV idx)),
BoundVar::Env(idx) => self.asm.op(chasm!(GET idx)),
};
Ok(())
}
fn bt_set(&mut self,
ret: bool,
code: &Value) -> Result<(), Error> {
let args = code.args().collect::<Vec<_>>();
use ValueKind::*;
use Arith2::*;
match &args[..] {
[Value { kind: Symbol(dst), src }, init] => {
let bound = self.get_var_idx(*dst, src)?;
if let BoundVar::Local(idx) = bound {
let mut inplace_op = |op, val: i64| {
self.asm.add(op, &[idx.into(), val.into()]);
if ret { self.asm_op(chasm!(MOV idx)) }
};
match init.bt_arith2() {
Some(Add(Value { kind: Symbol(sym), .. },
Value { kind: Int(num), .. })) |
Some(Add(Value { kind: Int(num), .. },
Value { kind: Symbol(sym), .. })) if sym == dst => {
inplace_op(INC, *num);
return Ok(())
}
Some(Sub(Value { kind: Symbol(sym), .. },
Value { kind: Int(num), .. })) if sym == dst => {
inplace_op(DEC, *num);
return Ok(())
}
_ => ()
}
}
self.compile(true, init)?;
if ret {
self.asm_op(chasm!(DUP));
}
self.asm_set_var_idx(&bound)
}
[obj, _] => err_src!(obj.src.clone(), TypeError,
op: Builtin::Set.sym(),
expect: Builtin::Symbol.sym(),
argn: 1,
got: obj.type_of()),
_ => err_src!(code.src.clone(), ArgError,
expect: ArgSpec::normal(2),
op: Builtin::Set.sym(),
got_num: args.len() as u32)
}
}
pub fn link(self) -> Result<Linked, Error> {
let len = self.asm.len() as isize;
let (asm, labels) = self.asm.link::<R8C>(&self.symtbl, len)?;
Ok((asm, labels, self.consts))
}
fn define_static(&mut self,
ret: bool,
name: SymID,
init: &Value) -> Result<(), Error> {
let mut cc = R8Compiler::new(self.vm);
cc.estack.push(Env::empty());
cc.compile(true, init)?;
let (asm, _, consts) = cc.link()?;
let pv = unsafe {
self.vm.add_and_run(asm, Some(consts))?
};
let idx = self.vm.mem.push_env(pv);
if ret {
self.asm.op(chasm!(GET idx));
}
self.with_env(|env| env.defenv(name, idx))
}
fn bt_define_static(&mut self,
ret: bool,
code: &Value) -> Result<(), Error> {
let args = code.args().collect::<Vec<_>>();
match &args[..] {
[Value { kind: ValueKind::Symbol(dst), .. }, src] =>
self.define_static(ret, *dst, src),
[obj, _] => err_src!(obj.src.clone(), TypeError,
op: Builtin::DefineStatic.sym(),
expect: Builtin::Symbol.sym(),
argn: 1,
got: obj.type_of()),
_ => err_src!(code.src.clone(), ArgError,
expect: ArgSpec::normal(2),
op: Builtin::DefineStatic.sym(),
got_num: args.len() as u32)
}
}
fn compile_value(&mut self, v: &Value) {
use ValueKind::*;
match &v.kind {
Symbol(obj) => { self.asm.op(chasm!(SYM *obj)); },
Nil => { self.asm.op(chasm!(NIL)); },
Int(x) => { self.asm.op(chasm!(PUSH *x)); },
Bool(true) => { self.asm.op(chasm!(BOOL 1)); },
Bool(false) => { self.asm.op(chasm!(BOOL 0)); },
Real(x) => { self.asm.op(chasm!(PUSHF (*x).to_bits())); },
String(s) => {
let idx = self.consts.len();
self.consts.push(NkSum::String(s.clone()));
self.asm.op(chasm!(CONSTREF idx));
}
Cons(_, _) => {
let mut len = 0;
for arg in v.iter() {
self.compile_value(arg);
len += 1;
}
self.asm.op(chasm!(LIST len));
}
}
}
fn bt_quote(&mut self, code: &Value) -> Result<(), Error> {
let args = code.args().collect::<Vec<_>>();
match &args[..] {
[obj] => self.compile_value(obj),
_ => return err_src!(code.src.clone(), ArgError,
expect: ArgSpec::normal(1),
op: Builtin::Quote.sym(),
got_num: args.len() as u32)
}
Ok(())
}
fn bt_quasi(&mut self, code: &Value) -> Result<(), Error> {
let qt = quasi_transform(
raise_inner(Builtin::Quasi.sym(), code.clone())?)?;
self.compile(true, &qt)
}
fn eval_when_compile<'z>(&mut self,
seq: impl Iterator<Item = &'z Value>
) -> Result<Value, Error> {
let mut cc = R8Compiler::new(self.vm);
cc.estack.push(Env::empty());
cc.compile_seq(true, seq)?;
let (asm, _, consts) = cc.link()?;
let res = unsafe {
self.vm.add_and_run(asm, Some(consts))?
};
Ok(unsafe { pv_to_value(res, &Source::none()) })
}
pub fn bt_eval_when(&mut self, code: &Value) -> Result<Value, Error> {
use Builtin::*;
let mut it = code.args();
let when = it.next()
.ok_or(error_src!(code.src.clone(), ArgError,
expect: ArgSpec::rest(1, 0),
got_num: 0,
op: EvalWhen.sym()))?;
let when_src = when.src.clone();
let when = when.sym()
.ok_or(error_src!(when_src.clone(), TypeError,
expect: Builtin::Symbol.sym(),
got: when.type_of(),
argn: 1,
op: EvalWhen.sym()))?;
let expect = vec![KwCompile.sym(), KwLoad.sym()];
match Builtin::from_sym(when) {
Some(KwCompile) => self.eval_when_compile(it),
Some(KwLoad) => unimplemented!("eval-when :load is not implemented yet."),
_ => err_src!(when_src, EnumError,
expect,
op: EvalWhen.sym(),
argn: 1,
got: when)
}
}
fn bt_lambda(&mut self, code: &Value) -> Result<(), Error> {
let ast::Lambda(ast::ArgList(mut spec, mut args), body) =
code.bt_lambda()?;
let outside = self.with_env(|env| env.as_map())?;
let mut body = body.clone();
self.vm.macroexpand_seq(&mut body)?;
let clz_scoper = ClzScoper::new(args.clone(), &outside);
let lowered = clz_scoper.scope_body(&body)?;
let num = LAMBDA_COUNT.fetch_add(1, Ordering::SeqCst);
let name = self.vm.mem.symdb.put(format!("<λ>::L{}:{}#{}",
code.src.line,
code.src.col,
num));
let mut num = 0;
for (var, bound) in lowered.iter() {
if let BoundVar::Local(idx) = bound {
args.push(*var);
self.asm_op(chasm!(MOV *idx));
num += 1;
}
}
spec.env = num;
self.asm_op(chasm!(CLZ name.id, num));
let mut cc = R8Compiler::new(self.vm);
cc.enter_fn(args, spec)?;
for (var, bound) in lowered.iter() {
if let BoundVar::Env(idx) = bound {
cc.with_env(|env| env.defenv(*var, *idx as usize))?;
}
}
cc.compile_seq(true, body.iter())?;
if spec.has_env() {
cc.asm_op(chasm!(CLZAV spec.sum_nargs() + 1, spec.env));
}
cc.leave_fn();
let linked = cc.link()?;
self.vm.add_func(name, linked, spec);
Ok(())
}
fn bt_var_idx(&mut self, code: &Value) -> Result<(), Error> {
let args = code.args().collect::<Vec<_>>();
match &args[..] {
[Value { kind: ValueKind::Symbol(var), src }] => {
let (sym, idx) = match self.get_var_idx(*var, src)? {
BoundVar::Local(idx) => (Builtin::KwLocal.sym(), idx),
BoundVar::Env(idx) => (Builtin::KwEnv.sym(), idx),
};
self.asm_op(chasm!(SYM sym));
self.asm_op(chasm!(PUSH idx));
self.asm_op(chasm!(CONS));
}
[x] => return err_src!(code.src.clone(), TypeError,
expect: Builtin::Symbol.sym(),
got: x.type_of(),
argn: 1,
op: Builtin::DebugVarIdx.sym()),
_ => return ArgSpec::normal(1).check(Builtin::DebugVarIdx.sym(),
args.len() as u16)
}
Ok(())
}
fn bt_vpush(&mut self, ret: bool, code: &Value) -> Result<(), Error> {
let nargs = code.nargs();
ArgSpec::normal(2).check(Builtin::Push.sym(), nargs)?;
let mut it = code.args();
let vec = it.next().unwrap();
let val = it.next().unwrap();
self.push(val)?;
if ret {
self.asm.op(chasm!(DUP));
}
self.push(vec)?;
self.asm.op(chasm!(VPUSH));
self.env_pop(nargs as usize)
}
fn compile_builtin(&mut self,
ret: bool,
op: Builtin,
code: &Value) -> Option<Result<(), Error>> {
use Builtin::*;
let mut gen_call = |opcode, opargs, spec| self.gen_call(ret,
op.sym(),
(opcode, opargs),
spec,
code.args());
macro_rules! may_ret {
($what:expr) => {{ if ret { $what } else { Ok(()) } }}
};
Some(match op {
Quote => may_ret!(self.bt_quote(code)),
Quasi => may_ret!(self.bt_quasi(code)),
Unquote | USplice => err_src!(code.src.clone(), OutsideContext,
ctx: Builtin::Quasi.sym(),
op: op.sym()),
If => self.bt_if(ret, code),
Next => self.bt_next(code),
Break => self.bt_break(code),
Let => self.bt_let(ret, code),
And => self.bt_and(ret, code),
Or => self.bt_or(ret, code),
Define => self.bt_define(ret, code),
DefineStatic => self.bt_define_static(ret, code),
Set => self.bt_set(ret, code),
Loop => self.bt_loop(ret, code.args()),
Not => may_ret!(self.bt_not(code)),
EvalWhen => self.bt_eval_when(code).map(|res| if ret {
self.compile_value(&res);
}),
Progn => self.compile_seq(ret, code.args()),
Lambda => may_ret!(self.bt_lambda(code)),
Cons => may_ret!(gen_call(r8c::OpName::CONS, &[], ArgSpec::normal(2))),
Car => may_ret!(gen_call(r8c::OpName::CAR, &[], ArgSpec::normal(1))),
Cdr => may_ret!(gen_call(r8c::OpName::CDR, &[], ArgSpec::normal(1))),
List => may_ret!(gen_call(r8c::OpName::LIST,
&[code.nargs().into()],
ArgSpec::any())),
Append => may_ret!(gen_call(r8c::OpName::APPEND,
&[code.nargs().into()],
ArgSpec::any())),
Vector => may_ret!(gen_call(r8c::OpName::VEC,
&[code.nargs().into()],
ArgSpec::any())),
Push => self.bt_vpush(ret, code),
Get => may_ret!(gen_call(r8c::OpName::VGET, &[], ArgSpec::normal(2))),
Len => may_ret!(gen_call(r8c::OpName::LEN, &[], ArgSpec::normal(1))),
Pop => gen_call(r8c::OpName::VPOP, &[], ArgSpec::normal(1)),
Add => may_ret!(self.binop(code, (ADD, &[]))),
Sub => may_ret!(self.binop(code, (SUB, &[]))),
Div => may_ret!(self.binop(code, (DIV, &[]))),
Mul => may_ret!(self.binop(code, (MUL, &[]))),
Gt => may_ret!(self.cmp_binop(code, (GT, &[]))),
Lt => may_ret!(self.cmp_binop(code, (LT, &[]))),
Lte => may_ret!(self.cmp_binop(code, (LTE, &[]))),
Gte => may_ret!(self.cmp_binop(code, (GTE, &[]))),
Eq => may_ret!(self.cmp_binop(code, (EQL, &[]))),
DebugVarIdx => may_ret!(self.bt_var_idx(code)),
_ => return None
})
}
fn compile_builtin_atom(&mut self, op: Builtin) -> Result<(), Error> {
use Builtin::*;
match op {
Nil => self.asm.op(chasm!(NIL)),
_ => self.asm.op(chasm!(SYM op.sym()))
};
Ok(())
}
#[inline]
pub fn compile_atom(&mut self, atom: &Value) -> Result<(), Error> {
use ValueKind::*;
match &atom.kind {
Int(x) => { self.asm.op(chasm!(PUSH *x)); },
Symbol(v) => if let Some(op_bt) = Builtin::from_sym(*v) {
self.compile_builtin_atom(op_bt)?;
} else if self.vm.sym_name(*v).starts_with(':') {
self.asm_op(chasm!(SYM *v));
} else if let Err(e) = self.asm_get_var(*v, &atom.src) {
if self.vm.get_func(*v).is_some() {
self.asm.op(chasm!(CLZ *v, 0));
} else {
return Err(e);
}
}
String(p) => {
let idx = self.consts.len() as i64;
self.consts.push(NkSum::String(p.clone()));
self.asm.op(chasm!(CONSTREF idx));
}
Bool(true) => { self.asm.op(chasm!(BOOL 1)); },
Bool(false) => { self.asm.op(chasm!(BOOL 0)); },
Nil => { self.asm.op(chasm!(NIL)); },
Real(x) => { self.asm.op(chasm!(PUSHF (*x).to_bits())); }
x => unimplemented!("{:?}", x),
}
Ok(())
}
#[inline]
pub fn compile_app(&mut self,
ret: bool,
op: SymID,
code: &Value) -> Result<(), Error> {
let mut get_bt = |op| Builtin::from_sym(op).and_then(|bt| {
self.compile_builtin(ret, bt, code)
});
if let Some(res) = get_bt(op) { res
} else if let Some(res) = self.vm.expand(code) { let code = res?;
self.compile(ret, &code)
} else if let Some(idx) = self.vm.get_sysfn(op) { let spec = ArgSpec::normal(SYSTEM_FUNCTIONS[idx].nargs);
self.gen_call(ret, op, (r8c::OpName::SYSCALL, &[idx.into()]),
spec, code.args())
} else if let Ok(()) = self.asm_get_var(op, &code.src) { self.with_env(|env| env.anon())?;
self.gen_call_nargs(ret, (r8c::OpName::CLZCALL,
&mut [0.into()]),
0, code.args())?;
self.env_pop(1)
} else { self.gen_call_nargs(ret, (r8c::OpName::VCALL,
&mut [op.into(), 0.into()]),
1, code.args())
}
}
fn compile(&mut self, ret: bool, code: &Value) -> Result<(), Error> {
if let Some(op) = code.op() {
self.compile_app(ret, op, &code)?;
} else if code.is_atom() {
if ret {
return self.compile_atom(&code);
}
} else {
let mut it = code.iter();
let op = it.next().unwrap();
if op.is_atom() {
return err_src!(op.src.clone(), TypeError,
expect: Builtin::Lambda.sym(),
got: op.type_of(),
argn: 0,
op: Builtin::Apply.sym())
}
self.compile(true, op)?;
self.with_env(|env| env.anon())?; self.gen_call_nargs(ret, (r8c::OpName::CLZCALL,
&mut [0.into()]),
0, it)?;
self.env_pop(1)?;
}
Ok(())
}
}