jsonpiler 0.10.4

a Json syntax programming language for Windows
Documentation
use super::utility::*;
use crate::prelude::*;
impl Pos<Parser> {
  pub(crate) fn parse_block(&mut self, is_top_level: bool) -> ParseErrOR<Json> {
    self.check_eof()?;
    let mut entries = vec![];
    let mut entry_pos: Option<Position> = None;
    if !is_top_level {
      self.expect(b'{')?;
    }
    loop {
      let result = self.skip_ws_comment(true);
      let is_eof = result.is_err();
      let is_separated = result.is_ok_and(|bool| bool);
      if (is_top_level && is_eof) || (!is_top_level && self.consume_if(b'}')?) {
        break;
      }
      if !entries.is_empty() && !is_separated && !is_eof {
        return Err(self.pos.with(ExpectedToken(TokenKind::Separate)));
      }
      let value = self.try_operator(0)?;
      if let Some(pos) = entry_pos {
        self.warn(pos, UselessLiteral, &self.val.file.clone());
      }
      if let Object(Lit(object)) = value.val {
        entries.extend(object);
      } else {
        entry_pos = Some(value.pos);
        entries.push((value.pos.with("value".to_owned()), value.pos.with(Array(Lit(vec![value])))));
      }
    }
    Ok(Object(Lit(entries)))
  }
  fn parse_call(&mut self) -> ParseErrOR<Pos<Json>> {
    let mut pos = self.pos;
    self.expect(b'(')?;
    self.skip_ws_comment(false)?;
    let mut args = vec![];
    if self.consume_if(b')')? {
      self.set_size(&mut pos);
      return Ok(pos.with(Array(Lit(args))));
    }
    loop {
      self.skip_ws_comment(false)?;
      args.push(self.try_operator(0)?);
      self.skip_ws_comment(false)?;
      let did_consume = self.consume_if(b',')?;
      if did_consume {
        self.skip_ws_comment(false)?;
      }
      if self.consume_if(b')')? {
        break;
      }
      if !did_consume {
        self.expect(b',')?;
      }
      self.skip_ws_comment(false)?;
    }
    self.set_size(&mut pos);
    Ok(pos.with(Array(Lit(args))))
  }
  fn parse_ident(&mut self) -> ParseErrOR<Pos<String>> {
    let mut pos = self.pos;
    while (self.pos.offset as usize) < self.val.text.len() {
      let byte = self.peek();
      if byte.is_ascii_whitespace() || byte.is_ascii_control() || b"#()[,]{:;}\"".contains(&byte) {
        break;
      }
      self.pos.offset += 1;
    }
    if pos.offset == self.pos.offset {
      return Err(pos.with(ExpectedIdent));
    }
    self.set_size(&mut pos);
    Ok(pos.with(self.get_slice(pos)?.into()))
  }
  pub(crate) fn parse_jspl(&mut self) -> ParseErrOR<Pos<Json>> {
    if self.skip_ws_comment(true).is_err() {
      return Ok(self.pos.with(Null(Lit(()))));
    }
    let mut pos = self.pos;
    let val = self.parse_block(true)?;
    self.set_size(&mut pos);
    if self.skip_ws_comment(true).is_err() {
      Ok(pos.with(val))
    } else {
      Err(self.pos.with(ExpectedToken(TokenKind::Eof)))
    }
  }
  fn skip_space_check_sep(&mut self) -> bool {
    while (self.pos.offset as usize) < self.val.text.len() {
      match self.peek() {
        b' ' | b'\t' => {
          self.pos.offset += 1;
          if self.check_eof().is_err() {
            return true;
          }
        }
        b'\n' | b'#' => return true,
        ws if ws.is_ascii_whitespace() => return true,
        _ => return false,
      }
    }
    true
  }
  pub(crate) fn skip_ws_comment(&mut self, is_block: bool) -> ParseErrOR<bool> {
    let mut is_separated = false;
    while (self.pos.offset as usize) < self.val.text.len() {
      if self.consume_if(b'#')? {
        let mut pos = self.pos;
        pos.offset -= 1;
        loop {
          let result = self.consume_if(b'\n').ok();
          if result.is_none_or(|boolean| boolean) {
            self.set_size(&mut pos);
            if result.is_some() {
              pos.size -= 1;
            }
            self.val.comments.insert(
              pos.offset,
              Comment { leading: is_separated, text: self.get_slice(pos)?.to_owned() },
            );
            self.pos.line += 1;
            is_separated = true;
            break;
          }
          self.pos.offset += 1;
        }
        continue;
      }
      match self.peek() {
        b'\n' => {
          is_separated = true;
          self.pos.line += 1;
          self.pos.offset += 1;
        }
        b';' if is_block => {
          is_separated = true;
          self.pos.offset += 1;
        }
        ws if ws.is_ascii_whitespace() => self.pos.offset += 1,
        _ => return Ok(is_separated),
      }
    }
    Err(self.eof_err())
  }
  fn try_concat_op(
    &mut self,
    prec: usize,
    operator: &mut Pos<String>,
    left: &mut Pos<Json>,
  ) -> ParseErrOR<()> {
    let right = self.try_operator(prec)?;
    let mut pos = left.pos;
    self.set_size(&mut pos);
    operator.pos.info = INFO_OP;
    if let Object(Lit(obj)) = &mut left.val
      && obj.len() == 1
      && obj[0].0.pos.info == INFO_OP
      && operator.val == obj[0].0.val
      && !matches!(obj[0].0.val.as_ref(), "<<" | ">>" | "%")
      && let Array(Lit(args)) = &mut obj[0].1.val
    {
      args.push(right);
    } else {
      let args = pos.with(Array(Lit(vec![take(left), right])));
      *left = pos.with(Object(Lit(vec![(operator.clone(), args)])));
    }
    Ok(())
  }
  fn try_operator(&mut self, min_prec: usize) -> ParseErrOR<Pos<Json>> {
    let mut left = self.try_parse_value()?;
    let mut unknown_op: Option<String> = None;
    loop {
      let save = self.pos;
      if self.skip_space_check_sep() {
        self.pos = save;
        break;
      }
      let Some(mut operator) = self.try_parse_ident() else {
        break;
      };
      if let Some(prec) = op_precedence(&operator.val) {
        if prec < min_prec || self.skip_space_check_sep() {
          self.pos = save;
          break;
        }
        self.try_concat_op(prec + 1, &mut operator, &mut left)?;
        continue;
      }
      match &unknown_op {
        None => unknown_op = Some(operator.val.clone()),
        Some(op) if op != &operator.val => {
          self.pos = save;
          break;
        }
        _ => (),
      }
      if self.skip_space_check_sep() {
        self.pos = save;
        break;
      }
      self.try_concat_op(0, &mut operator, &mut left)?;
    }
    Ok(left)
  }
  fn try_parse_ident(&mut self) -> Option<Pos<String>> {
    let saved = self.pos;
    self.skip_ws_comment(false).ok()?;
    let ident = self.parse_ident().ok()?;
    match ident.val.as_str() {
      "true" | "false" | "null" => {
        self.pos = saved;
        None
      }
      _ => Some(ident),
    }
  }
  fn try_parse_value(&mut self) -> ParseErrOR<Pos<Json>> {
    let mut pos = self.pos;
    let val = match self.peek() {
      b'"' => Str(Lit(self.parse_string()?)),
      b'0'..=b'9' => self.parse_number()?,
      b'-'
        if self
          .val
          .text
          .as_bytes()
          .get((self.pos.offset + 1) as usize)
          .is_some_and(u8::is_ascii_digit) =>
      {
        self.parse_number()?
      }
      b'[' => {
        self.pos.offset += 1;
        self.skip_ws_comment(false)?;
        if self.consume_if(b']')? {
          self.set_size(&mut pos);
          Array(Lit(vec![]))
        } else {
          let mut array = vec![];
          loop {
            array.push(self.try_operator(0)?);
            self.skip_ws_comment(false)?;
            let did_consume = self.consume_if(b',')?;
            if did_consume {
              self.skip_ws_comment(false)?;
            }
            if self.consume_if(b']')? {
              self.set_size(&mut pos);
              break Array(Lit(array));
            }
            if !did_consume {
              self.expect(b',')?;
            }
            self.skip_ws_comment(false)?;
          }
        }
      }
      b'{' => self.parse_block(false)?,
      _ => {
        let mut ident = self.parse_ident()?;
        let save = self.pos;
        let not_eof = self.skip_ws_comment(false).is_ok();
        if not_eof && self.peek() == b'(' {
          let args = self.parse_call()?;
          ident.pos.info = INFO_FUNC;
          Object(Lit(vec![(ident, args)]))
        } else if not_eof && self.consume_if(b':')? {
          self.skip_ws_comment(false)?;
          let args = self.try_operator(0)?;
          ident.pos.info = INFO_KEY_VAL;
          Object(Lit(vec![(ident, args)]))
        } else {
          self.pos = save;
          match ident.val.as_str() {
            "true" => Bool(Lit(true)),
            "false" => Bool(Lit(false)),
            "null" => Null(Lit(())),
            _ => Object(Lit(vec![(ident.pos.with("$".into()), ident.map(|str| Str(Lit(str))))])),
          }
        }
      }
    };
    self.set_size(&mut pos);
    Ok(pos.with(val))
  }
}