jsonpiler 0.10.4

a Json syntax programming language for Windows
Documentation
use crate::prelude::*;
pub(crate) type KeyVal = (Pos<String>, Pos<Json>);
#[derive(Debug, Clone)]
pub(crate) enum Json {
  Array(Bind<Vec<Pos<Json>>>),
  Bool(Bind<bool>),
  Float(Bind<f64>),
  Int(Bind<i64>),
  Null(Bind<()>),
  Object(Bind<Vec<KeyVal>>),
  Str(Bind<String>),
}
#[derive(Debug, Clone)]
pub(crate) enum Bind<T> {
  Lit(T),
  Var(Memory),
}
impl<T: Copy> Copy for Bind<T> {}
impl<T> fmt::Display for Bind<T> {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    match self {
      Var(memory) => write!(f, "{memory}"),
      Lit(_) => f.write_str(" (Literal)"),
    }
  }
}
#[derive(Debug, Clone, Default)]
pub(crate) enum JsonNoPos {
  ArrayN(Vec<JsonNoPos>),
  BoolN(bool),
  FloatN(f64),
  IntN(i64),
  #[default]
  NullN,
  ObjectN(Vec<(String, JsonNoPos)>),
  StrN(String),
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub(crate) enum JsonType {
  ArrayT,
  BoolT,
  CustomT(String),
  FloatT,
  FuncT(Box<Signature>),
  IntT,
  NullT,
  ObjectT,
  StrT,
}
impl Json {
  pub(crate) fn as_str(&self) -> Option<&str> {
    if let Str(Lit(string)) = self { Some(string) } else { None }
  }
  pub(crate) fn as_type(&self) -> JsonType {
    match self {
      Array(_) => ArrayT,
      Bool(_) => BoolT,
      Float(_) => FloatT,
      Int(_) => IntT,
      Null(_) => NullT,
      Object(_) => ObjectT,
      Str(_) => StrT,
    }
  }
  pub(crate) fn delete_pos(self) -> JsonNoPos {
    match self {
      Array(Lit(array)) => {
        ArrayN(array.into_iter().map(|pos_json| pos_json.val.delete_pos()).collect())
      }
      Bool(Lit(bind)) => BoolN(bind),
      Float(Lit(float)) => FloatN(float),
      Int(Lit(int)) => IntN(int),
      Null(Lit(_)) => NullN,
      Object(Lit(obj)) => ObjectN(
        obj.into_iter().map(|(pos_key, pos_val)| (pos_key.val, pos_val.val.delete_pos())).collect(),
      ),
      Str(Lit(bind)) => StrN(bind),
      Array(Var(_)) | Bool(Var(_)) | Float(Var(_)) | Int(Var(_)) | Null(Var(_))
      | Object(Var(_)) | Str(Var(_)) => StrN(self.describe()),
    }
  }
  pub(crate) fn describe(&self) -> String {
    format!(
      "{}{}",
      self.as_type().name(),
      match self {
        Bool(bind) => format!("{bind}"),
        Null(_) => String::new(),
        Float(bind) => format!("{bind}"),
        Object(bind) => format!("{bind}"),
        Int(bind) => format!("{bind}"),
        Str(bind) => format!("{bind}"),
        Array(bind) => format!("{bind}"),
      }
    )
  }
  pub(crate) fn memory(&self) -> Option<Memory> {
    match self {
      Int(Var(memory)) | Float(Var(memory)) | Str(Var(memory)) | Bool(Var(memory))
      | Array(Var(memory)) | Null(Var(memory)) | Object(Var(memory)) => Some(*memory),
      Array(_) | Bool(_) | Float(_) | Int(_) | Null(_) | Object(_) | Str(_) => None,
    }
  }
}
impl JsonNoPos {
  pub(crate) fn as_str(&self) -> Option<&str> {
    if let StrN(string) = self { Some(string) } else { None }
  }
  pub(crate) fn get(&self, key: &str) -> Option<&JsonNoPos> {
    if let ObjectN(obj) = self {
      let opt = obj.iter().find(|(ke, _)| ke == key).map(|(_, va)| va);
      if let Some(json) = opt {
        return Some(json);
      }
    }
    None
  }
  pub(crate) fn get_bool(&self, key: &str) -> Option<bool> {
    if let BoolN(boolean) = self.get(key)? { Some(*boolean) } else { None }
  }
  pub(crate) fn get_int(&self, key: &str) -> Option<i64> {
    if let IntN(int) = self.get(key)? { Some(*int) } else { None }
  }
  pub(crate) fn into_str(self) -> Option<String> {
    if let StrN(string) = self { Some(string) } else { None }
  }
  pub(crate) fn take(&mut self, key: &str) -> Option<JsonNoPos> {
    if let ObjectN(obj) = self {
      let opt = obj.iter_mut().find(|(ke, _)| ke == key).map(|(_, va)| va);
      if let Some(json) = opt {
        return Some(take(json));
      }
    }
    None
  }
}
impl JsonType {
  pub(crate) fn from_string(name: &str) -> Self {
    match name {
      "Str" => StrT,
      "Int" => IntT,
      "Float" => FloatT,
      "Null" => NullT,
      "Bool" => BoolT,
      "Object" => ObjectT,
      "Array" => ArrayT,
      unknown => CustomT(unknown.to_owned()),
    }
  }
  pub(crate) fn mem_type(&self, pos: Position) -> ErrOR<MemoryType> {
    match self {
      BoolT => Ok(MemoryType { heap: Value, size: Small(RB) }),
      FloatT | IntT | NullT => Ok(MemoryType { heap: Value, size: Small(RQ) }),
      StrT => Ok(MemoryType { heap: HeapPtr, size: Dynamic }),
      FuncT(_) | ArrayT | ObjectT => err!(pos, UnsupportedType(self.name())),
      CustomT(_) => err!(pos, UnknownType(self.name())),
    }
  }
  pub(crate) fn name(&self) -> String {
    match self {
      BoolT => "Bool",
      NullT => "Null",
      FloatT => "Float",
      ObjectT => "Object",
      FuncT(_) => "Func",
      IntT => "Int",
      StrT => "Str",
      ArrayT => "Array",
      CustomT(name) => name,
    }
    .into()
  }
  pub(crate) fn to_json(&self, pos: Position, addr: Address) -> ErrOR<Json> {
    let memory = Memory(addr, self.mem_type(pos)?);
    match self {
      StrT => Ok(Str(Var(memory))),
      IntT => Ok(Int(Var(memory))),
      FloatT => Ok(Float(Var(memory))),
      NullT => Ok(Null(Var(memory))),
      BoolT => Ok(Bool(Var(memory))),
      FuncT(_) | ArrayT | ObjectT => err!(pos, UnsupportedType(self.name())),
      CustomT(name) => err!(pos, UnknownType(name.clone())),
    }
  }
}
impl Pos<Json> {
  pub(crate) fn into_ident(mut self, name: &str) -> ErrOR<Pos<String>> {
    if let Object(Lit(obj)) = &mut self.val
      && obj.len() == 1
      && &obj[0].0.val == "$"
      && let Str(Lit(string)) = take(&mut obj[0].1.val)
    {
      Ok(self.pos.with(string))
    } else {
      Err(type_err(name.into(), vec![CustomT("Ident".into())], self.map_ref(Json::as_type)))
    }
  }
}
impl fmt::Display for JsonType {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    f.write_str(&self.name())
  }
}
impl fmt::Display for JsonNoPos {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    self.fmt_with_indent(f, 0)
  }
}
impl fmt::Display for Json {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    self.clone().delete_pos().fmt_with_indent(f, 0)
  }
}
impl JsonNoPos {
  fn fmt_with_indent(&self, fmter: &mut fmt::Formatter, indentation: usize) -> fmt::Result {
    match self {
      ArrayN(array) => {
        write!(fmter, "[")?;
        format_items(array, indentation, fmter, |(_f, item)| Ok(item))?;
        write!(fmter, "]")
      }
      ObjectN(obj) => {
        write!(fmter, "{{")?;
        format_items(obj, indentation, fmter, |(fmter2, (key, val))| {
          write!(fmter2, "{}: ", StrN(key.clone()))?;
          Ok(val)
        })?;
        write!(fmter, "}}")
      }
      BoolN(lit) => write!(fmter, "{lit}"),
      IntN(lit) => write!(fmter, "{lit}"),
      FloatN(lit) => write!(fmter, "{lit}"),
      StrN(lit) => write!(fmter, "\"{}\"", json_escape(lit)),
      Self::NullN => write!(fmter, "null"),
    }
  }
}
impl Default for Json {
  fn default() -> Self {
    Null(Lit(()))
  }
}
fn format_items<'a, T, F: Fn((&mut fmt::Formatter, &'a T)) -> Result<&'a JsonNoPos, fmt::Error>>(
  items: &'a [T],
  indentation: usize,
  fmter: &mut fmt::Formatter,
  task: F,
) -> fmt::Result {
  for (i, item) in items.iter().enumerate() {
    if i != 0 {
      write!(fmter, ",")?;
    }
    writeln!(fmter)?;
    indent(fmter, indentation + 1)?;
    let json = task((fmter, item))?;
    json.fmt_with_indent(fmter, indentation + 1)?;
  }
  if !items.is_empty() {
    writeln!(fmter)?;
    indent(fmter, indentation)
  } else {
    Ok(())
  }
}
fn indent(fmter: &mut fmt::Formatter, n: usize) -> fmt::Result {
  for _ in 0..n {
    write!(fmter, "  ")?;
  }
  Ok(())
}
pub(crate) fn json_escape(input: &str) -> String {
  let mut out = String::with_capacity(input.len());
  for char in input.chars() {
    match char {
      '"' => out.push_str("\\\""),
      '\\' => out.push_str("\\\\"),
      '\n' => out.push_str("\\n"),
      '\r' => out.push_str("\\r"),
      '\t' => out.push_str("\\t"),
      '\u{08}' => out.push_str("\\b"),
      '\u{0C}' => out.push_str("\\f"),
      ctrl if ctrl.is_control() => {
        out.push_str(&format!("\\u{:04x}", ctrl as u32));
      }
      _ => out.push(char),
    }
  }
  out
}