jsonpiler 0.7.4

a Json syntax programming language for Windows
Documentation
use crate::{
  Bind::Lit, CompilationErrKind::*, ErrOR, Json, JsonpilerErr::*, Parser, TokenKind, WithPos,
  parse_err,
};
impl Parser {
  pub(crate) fn parse_block(&mut self, is_top_level: bool) -> ErrOR<Json> {
    let pos = self.pos;
    self.check_eof()?;
    let mut exist_non_call = false;
    let mut entries = vec![];
    if !is_top_level {
      self.expect(b'{')?;
    }
    loop {
      let is_eof_or_not_sep = self.skip_block_ws_check_eof();
      if !is_top_level && self.source.get(self.pos.offset) == Some(&b'}') {
        let _: ErrOR<()> = self.advance(1);
        break;
      }
      let is_eof = is_eof_or_not_sep.is_err();
      if !entries.is_empty() && is_eof_or_not_sep.is_ok_and(|x| !x) {
        return parse_err!(self, ExpectedTokenError(TokenKind::NewLineOrSemiColon));
      }
      if is_eof {
        if is_top_level {
          break;
        }
        return parse_err!(self, pos, UnexpectedTokenError(TokenKind::Eof));
      }
      let val = self.try_multi_tokens()?;
      if exist_non_call {
        return parse_err!(self, val.pos, UnexpectedLiteral);
      }
      match val {
        WithPos { value: Json::Object(Lit(vec)), .. } => entries.extend(vec),
        WithPos { value: Json::Array(Lit(_)), .. } => {
          exist_non_call = true;
          entries.push((
            WithPos { pos: val.pos, value: "value".into() },
            WithPos { pos: val.pos, value: Json::Array(Lit(vec![val])) },
          ));
        }
        value => {
          exist_non_call = true;
          entries.push((WithPos { pos: value.pos, value: "value".into() }, value));
        }
      }
    }
    Ok(Json::Object(Lit(entries)))
  }
  fn parse_call(&mut self) -> ErrOR<WithPos<Json>> {
    let mut pos = self.pos;
    self.check_eof()?;
    self.expect(b'(')?;
    self.skip_ws_and_comment()?;
    let mut args = vec![];
    if self.consume_if(b')')? {
      pos.extend_to(self.pos.offset);
      return Ok(WithPos { pos, value: Json::Array(Lit(args)) });
    }
    loop {
      self.skip_ws_and_comment()?;
      args.push(self.try_multi_tokens()?);
      self.skip_ws_and_comment()?;
      if self.source.get(self.pos.offset) == Some(&b')') {
        let _: ErrOR<()> = self.advance(1);
        break;
      }
      self.expect(b',')?;
      self.skip_ws_and_comment()?;
    }
    pos.extend_to(self.pos.offset);
    Ok(WithPos { pos, value: Json::Array(Lit(args)) })
  }
  fn parse_ident(&mut self) -> ErrOR<WithPos<String>> {
    let mut pos = self.pos;
    self.check_eof()?;
    while self.pos.offset < self.source.len() {
      if self.check_eof().is_err() {
        break;
      }
      let byte = self.peek();
      if !(0x21..=0x7E).contains(&byte) {
        break;
      }
      match byte {
        b'(' | b')' | b'[' | b']' | b'{' | b'}' | b',' | b'"' | b':' | b';' => break,
        _ => self.pos.offset += 1,
      }
    }
    if pos.offset == self.pos.offset {
      return parse_err!(self, pos, InvalidIdentifier);
    }
    pos.extend_to(self.pos.offset);
    let Ok(ident) = String::from_utf8(self.source[pos.offset..self.pos.offset].to_vec()) else {
      return parse_err!(self, pos, InvalidChar);
    };
    Ok(WithPos { pos, value: ident })
  }
  fn parse_key(&mut self) -> ErrOR<WithPos<String>> {
    let ident = self.parse_ident()?;
    if ident.value.starts_with('$') {
      parse_err!(self, ident.pos, InvalidIdentifier)
    } else {
      match ident.value.as_str() {
        "true" | "false" | "null" => parse_err!(self, ident.pos, InvalidIdentifier),
        _ => Ok(ident),
      }
    }
  }
  fn skip_block_ws_check_eof(&mut self) -> ErrOR<bool> {
    let mut found_sep = false;
    while self.pos.offset < self.source.len() {
      if self.consume_if(b'#')? {
        while self.pos.offset < self.source.len() {
          if self.consume_if(b'\n')? {
            self.pos.line += 1;
            found_sep = true;
            break;
          }
          self.advance(1)?;
        }
        continue;
      }
      let ch = self.peek();
      if ch == b'\n' {
        self.pos.line += 1;
        found_sep = true;
        self.advance(1)?;
        continue;
      }
      if ch == b';' {
        found_sep = true;
        self.advance(1)?;
        continue;
      }
      if ch.is_ascii_whitespace() {
        self.advance(1)?;
        continue;
      }
      if !found_sep {
        return Ok(false);
      }
      return Ok(true);
    }
    parse_err!(self, UnexpectedTokenError(TokenKind::Eof))
  }
  fn skip_space(&mut self) -> bool {
    while self.pos.offset < self.source.len() {
      let byte = self.peek();
      match byte {
        b' ' | b'\t' => {
          if self.advance(1).is_err() {
            return true;
          }
        }
        b'\n' | b'\r' | b'#' => return true,
        _ if byte.is_ascii_whitespace() => return true,
        _ => return false,
      }
    }
    true
  }
  fn skip_ws_and_comment(&mut self) -> ErrOR<()> {
    self.check_eof()?;
    while self.pos.offset < self.source.len() {
      if self.consume_if(b'#')? {
        while self.pos.offset < self.source.len() {
          if self.consume_if(b'\n')? {
            self.pos.line += 1;
            break;
          }
          self.advance(1)?;
        }
        continue;
      }
      if !self.peek().is_ascii_whitespace() {
        return Ok(());
      }
      if self.peek() == b'\n' {
        self.pos.line += 1;
      }
      self.advance(1)?;
    }
    self.check_eof()
  }
  fn try_multi_tokens(&mut self) -> ErrOR<WithPos<Json>> {
    let val1 = self.try_parse_value()?;
    let mut saved = self.pos;
    let mut val1_pos = val1.pos;
    if self.skip_space() {
      self.pos = saved;
      return Ok(val1);
    }
    let Some(ident) = self.try_parse_ident() else {
      self.pos = saved;
      return Ok(val1);
    };
    if self.skip_space() {
      self.pos = saved;
      return Ok(val1);
    }
    let Ok(val2) = self.try_parse_value() else {
      self.pos = saved;
      return Ok(val1);
    };
    let mut operand_vec = vec![val1, val2];
    saved = self.pos;
    loop {
      if self.skip_space() {
        self.pos = saved;
        break;
      }
      let Some(rest_ident) = self.try_parse_ident() else {
        self.pos = saved;
        break;
      };
      if ident.value != rest_ident.value {
        self.pos = saved;
        break;
      }
      if self.skip_space() {
        self.pos = saved;
        break;
      }
      let Ok(rest_val) = self.try_parse_value() else {
        self.pos = saved;
        break;
      };
      saved = self.pos;
      operand_vec.push(rest_val);
    }
    val1_pos.extend_to(self.pos.offset);
    let array_val = Json::Array(Lit(operand_vec));
    let object_val = Json::Object(Lit(vec![(ident, WithPos { pos: val1_pos, value: array_val })]));
    Ok(WithPos { pos: val1_pos, value: object_val })
  }
  fn try_parse_ident(&mut self) -> Option<WithPos<String>> {
    let pos = self.pos;
    self.skip_ws_and_comment().ok()?;
    if let Ok(res) = self.parse_key() {
      Some(res)
    } else {
      self.pos = pos;
      None
    }
  }
  fn try_parse_value(&mut self) -> ErrOR<WithPos<Json>> {
    let mut pos = self.pos;
    let value = match self.peek() {
      b'"' => Json::String(Lit(self.parse_string()?)),
      b'0'..=b'9' => self.parse_number()?,
      b'-' if matches!(self.source.get(self.pos.offset + 1), Some(b'0'..=b'9')) => {
        self.parse_number()?
      }
      b'[' => {
        self.advance(1)?;
        let mut list = vec![];
        if self.peek() == b']' {
          let _: ErrOR<()> = self.advance(1);
          pos.extend_to(self.pos.offset);
          Json::Array(Lit(list))
        } else {
          loop {
            self.skip_ws_and_comment()?;
            list.push(self.try_multi_tokens()?);
            self.skip_ws_and_comment()?;
            if !self.consume_if(b',')? {
              break;
            }
          }
          self.skip_ws_and_comment()?;
          self.expect(b']')?;
          pos.extend_to(self.pos.offset);
          Json::Array(Lit(list))
        }
      }
      b'{' => self.parse_block(false)?,
      _ => {
        let ident = self.parse_ident()?;
        if ident.value.chars().nth(0) == Some('$') {
          #[expect(clippy::string_slice)]
          Json::Object(Lit(vec![(
            WithPos { pos: ident.pos, value: "$".into() },
            WithPos { pos: ident.pos, value: Json::String(Lit(ident.value[1..].into())) },
          )]))
        } else {
          let before_jspl_skip_ws = self.pos;
          let unreached_eof = self.skip_ws_and_comment().is_ok();
          if unreached_eof && self.peek() == b'(' {
            let args = self.parse_call()?;
            Json::Object(Lit(vec![(ident, args)]))
          } else if unreached_eof && self.consume_if(b':')? {
            self.skip_ws_and_comment()?;
            let args = self.try_multi_tokens()?;
            Json::Object(Lit(vec![(ident, args)]))
          } else {
            self.pos = before_jspl_skip_ws;
            match ident.value.as_str() {
              "true" => Json::Bool(Lit(true)),
              "false" => Json::Bool(Lit(false)),
              "null" => Json::Null,
              _ => Json::String(Lit(ident.value)),
            }
          }
        }
      }
    };
    pos.extend_to(self.pos.offset);
    Ok(WithPos { pos, value })
  }
}