jsonpiler 0.11.0

a Json syntax programming language for Windows
Documentation
use crate::prelude::*;
built_in! {self, func, scope, control;
  {"break", COMMON, Exact(0),
    f_break => {self.loop_control(false, func, scope) },
    f_break_a => {self.loop_control(false, func, scope) }
  },
  {"continue", COMMON, Exact(0),
    f_continue => { self.loop_control(true, func, scope) },
    f_continue_a => { self.loop_control(true, func, scope) }
  },
  {"if", SPECIAL, AtLeast(1),
    f_if => { self.eval_if_branches(func, scope) },
    f_if_a => { self.eval_if_branches(func, scope) }
  },
  {"while", SP_SCOPE, Exact(2),
    f_while => { self.while_loop(func, scope) },
    f_while_a => { self.while_loop(func, scope) }
  }
}
impl Jsonpiler {
  fn check_multi_if(
    &mut self,
    bool: Pos<Bind<bool>>,
    found_true: &mut bool,
    func: &Pos<BuiltIn>,
  ) -> ErrOR<Option<Memory>> {
    match bool.val {
      Lit(reachable) => {
        if reachable {
          if func.val.len == 1 {
            self.warn(bool.pos.with(UselessIfTrue))?;
          }
          if func.val.nth != func.val.len {
            self.warn(bool.pos.with(EarlyElse))?;
          }
          *found_true = true;
        } else {
          self.warn(bool.pos.with(UnreachableIf))?;
        }
        Ok(None)
      }
      Var(mem) => Ok(Some(mem)),
    }
  }
  fn check_single_if(&mut self, bool: Pos<Bind<bool>>) -> ErrOR<Option<Memory>> {
    match bool.val {
      Lit(reachable) => {
        if reachable {
          self.warn(bool.pos.with(UselessIfTrue))?;
        } else {
          self.warn(bool.pos.with(UnreachableIf))?;
        }
        Ok(None)
      }
      Var(mem) => Ok(Some(mem)),
    }
  }
  fn eval_if_branches(&mut self, func: &mut Pos<BuiltIn>, scope: &mut Scope) -> ErrOR<Json> {
    let end = self.id();
    let mut found_true = false;
    let mut arg = func.arg()?;
    let mut if_expr = if let Array(_, Lit(x)) = arg.val {
      arg.pos.with(x)
    } else {
      let cond = self.eval_with_scope(take(&mut arg), scope)?;
      let condition = unwrap_arg!(cond, "`if` condition", vec![BoolT], (Bool(x)) => x);
      func.validate_args(Exact(2))?;
      let mem_opt = self.check_single_if(condition)?;
      let then_lbl = self.id();
      self.eval_if_cond(condition, then_lbl, scope)?;
      let then = (then_lbl, func.arg()?, mem_opt);
      let (result_type, result_json, _) = self.eval_if_expr(then, end, None, func, scope)?;
      scope.push_lbl(end);
      if result_type != NullT && !found_true {
        return err!(func.pos, IfNoTrueBranch);
      }
      return Ok(result_json);
    };
    let mut then_vec = vec![];
    for _ in 1..=func.val.len {
      if if_expr.val.len() != 2 {
        return err!(
          if_expr.pos,
          ArityErr {
            name: "`if` expression".into(),
            expected: Exact(2),
            actual: len_u32(&if_expr.val)?
          }
        );
      }
      let mut cond = if_expr.val.remove(0);
      cond = self.eval_with_scope(take(&mut cond), scope)?;
      let condition = unwrap_arg!(cond, "`if` condition", vec![BoolT], (Bool(x)) => x);
      let mem_opt = self.check_multi_if(condition, &mut found_true, func)?;
      let then_lbl = self.id();
      self.eval_if_cond(condition, then_lbl, scope)?;
      then_vec.push((then_lbl, if_expr.val.remove(0), mem_opt));
      if func.val.nth != func.val.len {
        if_expr = array_arg!(func, None, Some(2), (Lit(x)) => x);
      }
    }
    let Some(before) = then_vec
      .into_iter()
      .try_fold(None, |before, then| self.eval_if_expr(then, end, before, func, scope).map(Some))?
    else {
      return Err(ArgNotFound(func.val.name.clone(), 1).into());
    };
    scope.push_lbl(end);
    if before.0 != NullT && !found_true {
      return err!(func.pos, IfNoTrueBranch);
    }
    Ok(before.1)
  }
  fn eval_if_cond(
    &mut self,
    cond: Pos<Bind<bool>>,
    then_lbl: LabelId,
    scope: &mut Scope,
  ) -> ErrOR<()> {
    if self.flags.a64 {
      scope.ee_a(vec![load_bool_a(X0, cond.val)?, vec![TstRb(X0)]])
    } else {
      scope.ee_x(vec![load_bool_x(Rax, cond.val)?, vec![TestRR(S1, Rax)]])
    }?;
    scope.push_jcc(Ne, then_lbl);
    Ok(())
  }
  fn eval_if_expr(
    &mut self,
    (then_lbl, expr, mem_opt): (LabelId, Pos<Json>, Option<Memory>),
    end: LabelId,
    before: Option<(JsonType, Json, Option<Memory>)>,
    func: &mut Pos<BuiltIn>,
    scope: &mut Scope,
  ) -> ErrOR<(JsonType, Json, Option<Memory>)> {
    func.push_free_tmp(mem_opt);
    scope.push_jmp(end);
    scope.push_lbl(then_lbl);
    scope.locals.push(BTreeMap::new());
    let json = self.eval(expr, scope)?;
    let json_type = json.val.as_type();
    let (result_type, result, result_mem_opt) = match before {
      Some((expected, e_json, e_mem)) => {
        if expected != json_type {
          let actual = json.map_ref(Json::as_type);
          return Err(type_err(fmt_ret_val("if"), vec![expected], actual));
        }
        (expected, e_json, e_mem)
      }
      None if json_type == NullT => (NullT, Null(Lit(())), None),
      None => {
        let mem_type = json_type.mem_type(json.pos)?;
        let size = mem_type.reg_size();
        let mem = Memory(scope.alloc_n(size)?.0, mem_type);
        let result = json_type.to_json(json.pos, mem.0)?;
        (json_type, result, Some(mem))
      }
    };
    let Some(mem) = result_mem_opt else {
      self.drop_json(&json.val, false, scope)?;
      self.drop_scope(scope)?;
      return Ok((result_type, result, None));
    };
    if self.flags.a64 {
      scope.ee_a(vec![self.load_json_a(X0, &json, Some(scope.id))?, store_a(mem, X1, X0)?])?;
    } else {
      scope.ee_x(vec![self.load_json_x(Rax, &json, Some(scope.id))?, store_x(mem, Rcx, Rax)?])?;
    }
    self.drop_json(&json.val, false, scope)?;
    self.drop_scope(scope)?;
    Ok((result_type, result, Some(mem)))
  }
  fn eval_with_scope(&mut self, expr: Pos<Json>, scope: &mut Scope) -> ErrOR<Pos<Json>> {
    scope.locals.push(BTreeMap::new());
    let value = self.eval(expr, scope)?;
    self.drop_scope(scope)?;
    Ok(value)
  }
  fn loop_control(
    &mut self,
    is_continue: bool,
    func: &mut Pos<BuiltIn>,
    scope: &mut Scope,
  ) -> ErrOR<Json> {
    let Some(&(start, end, idx)) = scope.loop_labels.last() else {
      return err!(func.pos, OutSideErr { name: func.val.name.clone(), place: "loop" });
    };
    let mems: Vec<_> = scope.locals[idx..]
      .iter()
      .flat_map(BTreeMap::values)
      .filter_map(|local| local.val.val.mem())
      .collect();
    for mem in mems {
      self.heap_free(mem, scope)?;
    }
    let lbl = if is_continue { start } else { end };
    scope.push_jmp(lbl);
    Ok(Null(Lit(())))
  }
  fn while_loop(&mut self, func: &mut Pos<BuiltIn>, scope: &mut Scope) -> ErrOR<Json> {
    let mut arg = func.arg()?;
    let start = self.id();
    let end = self.id();
    scope.loop_labels.push((start, end, scope.locals.len()));
    scope.push_lbl(start);
    arg = self.eval(take(&mut arg), scope)?;
    func.push_free_tmp(arg.val.mem());
    let cond = unwrap_arg!(arg, "`while` condition", vec![BoolT], (Bool(x)) => x);
    match cond.val {
      Lit(reachable) => {
        if !reachable {
          self.warn(cond.pos.with(UnreachableWhile))?;
        }
      }
      Var(mem) => {
        if self.flags.a64 {
          scope.ee_a(vec![load_a(X0, mem)?, vec![TstRb(X0)]])?;
        } else {
          scope.ee_x(vec![load_x(Rax, mem)?, vec![TestRR(S1, Rax)]])?;
        }
        scope.push_jcc(E, end);
      }
    }
    let json = self.eval_with_scope(func.arg()?, scope)?.val;
    self.drop_json(&json, false, scope)?;
    scope.push_jmp(start);
    scope.push_lbl(end);
    scope.loop_labels.pop();
    Ok(Null(Lit(())))
  }
}