jsonpiler 0.7.4

a Json syntax programming language for Windows
Documentation
use crate::{
  Arity::{AtLeast, Exactly, NoArgs},
  Bind::{self, Lit, Var},
  CompilationErrKind::*,
  ErrOR, FuncInfo,
  Inst::{self, *},
  Json, Jsonpiler,
  JsonpilerErr::*,
  Label,
  LogicByteOpcode::*,
  Position,
  Register::*,
  ScopeInfo, WithPos, built_in, err, take_arg,
  utility::{args_type_error, mov_float_reg, mov_float_xmm, mov_int, mov_q, take_float, take_int},
};
built_in! {self, _func, scope, arithmetic;
  abs => {"abs", COMMON, Exactly(1), {
    let arg = _func.arg()?;
    if let Json::Int(int) = arg.value {
      mov_int(&int, Rax, scope);
      scope.extend(&[
        Custom(&Jsonpiler::CQO),
        LogicRR(Xor, Rax, Rdx),
        SubRR(Rax, Rdx)
      ]);
      Ok(Json::Int(Var(scope.mov_tmp(Rax)?)))
    } else if let Json::Float(float) = arg.value {
      const BTR_RAX_63: &[u8] = &[0x48, 0x0F, 0xBA, 0xF0, 0x3F];
      mov_float_reg(&float, Rax, scope);
      scope.push(Custom(&BTR_RAX_63));
      Ok(Json::Float(Var(scope.mov_tmp(Rax)?)))
    } else {
      Err(args_type_error(1, &_func.name, "Int` or `Float".into(), &arg))
  }  }},
  add => {"+", COMMON, AtLeast(2), {
    arithmetic_template(
      &(vec![AddRR(Rax, Rcx)], AddSd(Rax, Rcx)),
      (&i64::wrapping_add, &i64::wrapping_add),
      _func, scope, 0, &|_| Ok(None), &||Some(IncR(Rax))
    )
  }},
  div => {"/", COMMON, AtLeast(2), {
    arithmetic_template(
      &(vec![Custom(&Jsonpiler::CQO), IDivR(Rcx)], DivSd(Rax, Rcx)),
      (&i64::wrapping_div, &i64::wrapping_mul),
      _func, scope, 1, &|pos| err!(self, pos, ZeroDivisionError), &||None)
  }},
  float => {"Float", COMMON, Exactly(1), {
    take_int(Rax, _func, scope)?;
    scope.push(CvtSi2Sd(Rax, Rax));
    scope.mov_tmp_xmm(Rax)
  }},
  int => {"Int", COMMON, Exactly(1), {
    take_float(Rax, Rax, _func, scope)?;
    scope.push(CvtTSd2Si(Rax, Rax));
    Ok(Json::Int(Var(scope.mov_tmp(Rax)?)))
  }},
  minus => {"-", COMMON, AtLeast(1), {
    if _func.len == 1 {
      match _func.arg()? {
      WithPos { value: Json::Int(int), .. } => {
        mov_int(&int, Rax, scope);
        scope.push(NegR(Rax));
        Ok(Json::Int(Var(scope.mov_tmp(Rax)?)))
      }
      WithPos { value: Json::Float(float), .. } => {
        const BTC_RAX_63: &[u8] = &[0x48, 0x0F, 0xBA, 0xF8, 0x3F];
        mov_float_reg(&float, Rax, scope);
        scope.push(Custom(&BTC_RAX_63));
        Ok(Json::Float(Var(scope.mov_tmp(Rax)?)))
      }
      other => {
        Err(args_type_error(1, &_func.name, "Int` or `Float".into(), &other))
      }
    }
    } else {
      arithmetic_template(&(vec![SubRR(Rax, Rcx)], SubSd(Rax, Rcx)), (&i64::wrapping_sub, &i64::wrapping_add), _func, scope, 0, &|_| Ok(None), &||Some(DecR(Rax)))
    }
  }},
  mul => {"*", COMMON, AtLeast(2), {
    arithmetic_template(&(vec![IMulRR(Rax, Rcx)], MulSd(Rax, Rcx)), (&i64::wrapping_mul, &i64::wrapping_mul), _func, scope, 1, &|_| Ok(Some(0)), &||None)
  }},
  random => {"random", COMMON, NoArgs, {
    scope.push(Call(self.get_random()?));
    Ok(Json::Int(Var(scope.mov_tmp(Rax)?)))
  }},
  rem => {"%", COMMON, Exactly(2), {
    let int1 = take_arg!(self, _func, (Int(x)) => x);
    let int2 = take_arg!(self, _func, (Int(x)) => x);
    if let (Lit(l_int1), Lit(l_int2)) = (&int1.value, &int2.value) {
      if *l_int2 == 0 {
        return err!(self, int2.pos, ZeroDivisionError);
      }
      return Ok(Json::Int(Lit(l_int1.wrapping_rem(*l_int2))));
    }
    mov_int(&int1.value, Rax, scope);
    match int2.value {
      Lit(l_int) => {
        if l_int == 0 {
          return err!(self, int2.pos, ZeroDivisionError);
        }
        mov_int(&Lit(l_int), Rcx, scope);
      }
      Var(label) => {
        scope.push(mov_q(Rcx, label.mem));
      }
    }
    scope.push(Custom(&Jsonpiler::CQO));
    scope.push(IDivR(Rcx));
    Ok(Json::Int(Var(scope.mov_tmp(Rdx)?)))
  }},
}
type Op = dyn Fn(i64, i64) -> i64;
fn arithmetic_template(
  op_inst: &(Vec<Inst>, Inst), ops: (&Op, &Op), func: &mut FuncInfo, scope: &mut ScopeInfo,
  ident_elem: i64, case_zero: &impl Fn(Position) -> ErrOR<Option<i64>>,
  case_one: &impl Fn() -> Option<Inst>,
) -> ErrOR<Json> {
  match func.arg()? {
    WithPos { value: Json::Int(int), .. } => {
      let mut int_vec = vec![];
      for _ in 1..func.len {
        int_vec.push(take_arg!(self, func, (Int(x)) => x).value);
      }
      let (first, vars, acc) =
        constant_fold(&int, int_vec, ops, ident_elem, &|| case_zero(func.pos))?;
      if first.is_none() && vars.is_empty() {
        return Ok(Json::Int(Lit(acc)));
      }
      if let Some(lbl) = first {
        if acc == 0 {
          if let Some(ret_val) = case_zero(func.pos)? {
            return Ok(Json::Int(Lit(ret_val)));
          }
          mov_int(&Var(lbl), Rax, scope);
        } else if acc == 1 {
          mov_int(&Var(lbl), Rax, scope);
          if let Some(inst) = case_one() {
            scope.push(inst);
          }
        } else {
          mov_int(&Var(lbl), Rax, scope);
          mov_int(&Lit(acc), Rcx, scope);
          scope.extend(&op_inst.0);
        }
      } else {
        mov_int(&Lit(acc), Rax, scope);
      }
      for var in vars {
        mov_int(&Var(var), Rcx, scope);
        scope.extend(&op_inst.0);
      }
      Ok(Json::Int(Var(scope.mov_tmp(Rax)?)))
    }
    WithPos { value: Json::Float(float), .. } => {
      mov_float_xmm(&float, Rax, Rax, scope)?;
      for _ in 1..func.len {
        take_float(Rcx, Rax, func, scope)?;
        scope.push(op_inst.1.clone());
      }
      scope.mov_tmp_xmm(Rax)
    }
    other => Err(args_type_error(1, &func.name, "Int` or `Float".into(), &other)),
  }
}
fn constant_fold(
  first: &Bind<i64>, rest: Vec<Bind<i64>>, ops: (&Op, &Op), ident_elem: i64,
  case_zero: &impl Fn() -> ErrOR<Option<i64>>,
) -> ErrOR<(Option<Label>, Vec<Label>, i64)> {
  let mut vars = vec![];
  let mut acc = ident_elem;
  for bind in rest {
    match bind {
      Lit(l_int) => acc = ops.1(acc, l_int),
      Var(lbl) => vars.push(lbl),
    }
  }
  match first {
    Lit(l_int) => {
      if acc == 0
        && let Some(ret_val) = case_zero()?
      {
        return Ok((None, vars, ret_val));
      }
      acc = ops.0(*l_int, acc);
      Ok((None, vars, acc))
    }
    Var(lbl) => Ok((Some(*lbl), vars, acc)),
  }
}