Exemplo n.º 1
0
 def single_quoted_string(self, p):
     raw_value = p[0]
     contents = raw_value[1:-1]
     terminator_in_string = re.search(
         r'(?<!\\)([\u000D\u2028\u2029]|(?<!\r)\n)', contents)
     if terminator_in_string:
         end = terminator_in_string.span()[0]
         before_terminator = terminator_in_string.string[:end]
         tok = p._slice[0]
         pos = tok.index + len(before_terminator)
         doc = tok.doc
         lineno = doc.count('\n', 0, pos) + 1
         colno = pos - doc.rfind('\n', 0, pos) + 1
         index = pos + 1
         errmsg = f"Illegal line terminator (line {lineno} column {colno} (char {index}) without continuation"
         self.errors.append(JSON5DecodeError(errmsg, tok))
     contents = re.sub(r'\\(\r\n|[\u000A\u000D\u2028\u2029])', '', contents)
     try:
         contents = re.sub(r'(\\x[a-fA-F0-9]{0,2}|\\u[0-9a-fA-F]{4})',
                           latin_unicode_escape_replace, contents)
     except JSON5DecodeError as exc:
         self.errors.append(JSON5DecodeError(exc.args[0], p._slice[0]))
     try:
         contents = re.sub(r'\\(0\d|.)', replace_escape_literals, contents)
     except JSON5DecodeError as exc:
         self.errors.append(JSON5DecodeError(exc.args[0], p._slice[0]))
     return SingleQuotedString(contents,
                               raw_value=raw_value,
                               tok=p._slice[0])
Exemplo n.º 2
0
 def number(self, p):
     self.errors.append(
         JSON5DecodeError("Invalid integer literal. Octals are not allowed",
                          p._slice[0]))
     raw_value = p[0]
     if re.search(r'[89]+', raw_value):
         self.errors.append(
             JSON5DecodeError(
                 "Invalid octal format. Octal digits must be in range 0-7",
                 p._slice[0]))
         return Integer(raw_value=oct(0), is_octal=True, tok=p._slice[0])
     return Integer(raw_value, is_octal=True, tok=p._slice[0])
Exemplo n.º 3
0
def replace_escape_literals(matchobj):
    s = matchobj.group(0)
    if s.startswith('\\0') and len(s) == 3:
        raise JSON5DecodeError("'\\0' MUST NOT be followed by a decimal digit",
                               None)
    seq = matchobj.group(1)
    return ESCAPE_SEQUENCES.get(seq, seq)
Exemplo n.º 4
0
def _latin_escape_replace(s):
    if s.startswith('\\x') and len(s) != 4:
        raise JSON5DecodeError(
            "'\\x' MUST be followed by two hexadecimal digits", None)
    val = ast.literal_eval(f'"{s}"')
    if val == '\\':
        val = '\\\\'  # this is important; the subsequent regex will sub it back to \\
    return val
Exemplo n.º 5
0
 def identifier(self, p):
     raw_value = p[0]
     name = re.sub(r'\\u[0-9a-fA-F]{4}', unicode_escape_replace, raw_value)
     pattern = r'[\w_\$]([\w_\d\$\p{Pc}\p{Mn}\p{Mc}\u200C\u200D])*'
     if not re.fullmatch(pattern, name):
         self.errors.append(
             JSON5DecodeError("Invalid identifier name", p._slice[0]))
     return Identifier(name=name, raw_value=raw_value, tok=p._slice[0])
Exemplo n.º 6
0
    def error(self, token):
        if token:
            if self.expecting:
                expected = self.expecting[-1]

                message = f"Syntax Error. Was expecting {' or '.join(expected)}"
            else:
                message = 'Syntax Error'

            self.errors.append(JSON5DecodeError(message, token))
            try:
                return next(self.tokens)
            except StopIteration:
                # EOF
                class tok:
                    type = '$end'
                    value = None
                    lineno = None
                    index = None

                return JSON5Token(tok(), None)
        elif self.last_token:
            doc = self.last_token.doc
            pos = len(doc)
            lineno = doc.count('\n', 0, pos) + 1
            colno = pos - doc.rfind('\n', 0, pos)
            message = (f'Expecting value. Unexpected EOF at: '
                       f'line {lineno} column {colno} (char {pos})')
            if self.expecting:
                expected = self.expecting[-1]
                message += f'. Was expecting {f" or ".join(expected)}'
            self.errors.append(JSON5DecodeError(message, None))
        else:
            #  Empty file
            self.errors.append(
                JSON5DecodeError('Expecting value. Received unexpected EOF',
                                 None))
Exemplo n.º 7
0
 def array_values(self, p):
     ret = [
         p.first_array_value,
     ]
     num_values = len(p.subsequent_array_value)
     for index, value in enumerate(p.subsequent_array_value):
         if isinstance(value, TrailingComma):
             if index + 1 != num_values:
                 self.errors.append(
                     JSON5DecodeError(
                         "Syntax Error: multiple trailing commas",
                         value.tok))
                 return ret, value
             return ret, value
         else:
             ret.append(value)
     return ret, None
Exemplo n.º 8
0
 def key_value_pairs(self, p):
     ret = [
         p.first_key_value_pair,
     ]
     num_sqvp = len(p.subsequent_key_value_pair)
     for index, value in enumerate(p.subsequent_key_value_pair):
         if isinstance(value, TrailingComma):
             if index + 1 != num_sqvp:
                 offending_token = value.tok
                 self.errors.append(
                     JSON5DecodeError(
                         "Syntax Error: multiple trailing commas",
                         offending_token))
             return ret, value
         else:
             ret.append(value)
     return ret, None
Exemplo n.º 9
0
    def parse(self, tokens):
        tokens = self._token_gen(tokens)
        model = super().parse(tokens)
        if self.errors:
            if len(self.errors) > 1:
                primary_error = self.errors[0]
                msg = "There were multiple errors parsing the JSON5 document.\n" \
                      "The primary error was: \n\t{}\n" \
                      "Additionally, the following errors were also detected:\n\t{}"

                num_additional_errors = len(self.errors) - 1
                additional_errors = '\n\t'.join(err.args[0]
                                                for err in self.errors[1:6])
                if num_additional_errors > 5:
                    additional_errors += f'\n\t{num_additional_errors - 5} additional error(s) truncated'
                msg = msg.format(primary_error.args[0], additional_errors)
                err = JSON5DecodeError(msg, None)
                err.lineno = primary_error.lineno
                err.token = primary_error.token
                err.index = primary_error.index
                raise err
            else:
                raise self.errors[0]
        return model
Exemplo n.º 10
0
 def error(self, t):
     raise JSON5DecodeError(
         f'Illegal character {t.value[0]!r} at index {self.index}', None)