blob: 87cb75855ff3124a9e3bf4218269d5db61579f60 [file] [log] [blame]
/*
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*/
#include <proton/parser.h>
#include <proton/scanner.h>
#include <proton/error.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "platform.h"
struct pn_parser_t {
pn_scanner_t *scanner;
char *atoms;
size_t size;
size_t capacity;
int error_code;
};
pn_parser_t *pn_parser()
{
pn_parser_t *parser = (pn_parser_t *) malloc(sizeof(pn_parser_t));
if (parser != NULL) {
parser->scanner = pn_scanner();
parser->atoms = NULL;
parser->size = 0;
parser->capacity = 0;
}
return parser;
}
static void pni_parser_ensure(pn_parser_t *parser, size_t size)
{
while (parser->capacity - parser->size < size) {
parser->capacity = parser->capacity ? 2 * parser->capacity : 1024;
parser->atoms = (char *) realloc(parser->atoms, parser->capacity);
}
}
int pn_parser_err(pn_parser_t *parser, int code, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
int err = pn_scanner_verr(parser->scanner, code, fmt, ap);
va_end(ap);
return err;
}
int pn_parser_errno(pn_parser_t *parser)
{
return pn_scanner_errno(parser->scanner);
}
const char *pn_parser_error(pn_parser_t *parser)
{
return pn_scanner_error(parser->scanner);
}
void pn_parser_free(pn_parser_t *parser)
{
if (parser) {
pn_scanner_free(parser->scanner);
free(parser->atoms);
free(parser);
}
}
static int pni_parser_shift(pn_parser_t *parser)
{
return pn_scanner_shift(parser->scanner);
}
static pn_token_t pni_parser_token(pn_parser_t *parser)
{
return pn_scanner_token(parser->scanner);
}
static int pni_parser_value(pn_parser_t *parser, pn_data_t *data);
static int pni_parser_descriptor(pn_parser_t *parser, pn_data_t *data)
{
if (pni_parser_token(parser).type == PN_TOK_AT) {
int err = pni_parser_shift(parser);
if (err) return err;
err = pn_data_put_described(data);
if (err) return pn_parser_err(parser, err, "error writing described");
pn_data_enter(data);
for (int i = 0; i < 2; i++) {
err = pni_parser_value(parser, data);
if (err) return err;
}
pn_data_exit(data);
return 0;
} else {
return pn_parser_err(parser, PN_ERR, "expecting '@'");
}
}
static int pni_parser_map(pn_parser_t *parser, pn_data_t *data)
{
if (pni_parser_token(parser).type == PN_TOK_LBRACE) {
int err = pni_parser_shift(parser);
if (err) return err;
err = pn_data_put_map(data);
if (err) return pn_parser_err(parser, err, "error writing map");
pn_data_enter(data);
if (pni_parser_token(parser).type != PN_TOK_RBRACE) {
while (true) {
err = pni_parser_value(parser, data);
if (err) return err;
if (pni_parser_token(parser).type == PN_TOK_EQUAL) {
err = pni_parser_shift(parser);
if (err) return err;
} else {
return pn_parser_err(parser, PN_ERR, "expecting '='");
}
err = pni_parser_value(parser, data);
if (err) return err;
if (pni_parser_token(parser).type == PN_TOK_COMMA) {
err = pni_parser_shift(parser);
if (err) return err;
} else {
break;
}
}
}
pn_data_exit(data);
if (pni_parser_token(parser).type == PN_TOK_RBRACE) {
return pni_parser_shift(parser);
} else {
return pn_parser_err(parser, PN_ERR, "expecting '}'");
}
} else {
return pn_parser_err(parser, PN_ERR, "expecting '{'");
}
}
static int pni_parser_list(pn_parser_t *parser, pn_data_t *data)
{
int err;
if (pni_parser_token(parser).type == PN_TOK_LBRACKET) {
err = pni_parser_shift(parser);
if (err) return err;
err = pn_data_put_list(data);
if (err) return pn_parser_err(parser, err, "error writing list");
pn_data_enter(data);
if (pni_parser_token(parser).type != PN_TOK_RBRACKET) {
while (true) {
err = pni_parser_value(parser, data);
if (err) return err;
if (pni_parser_token(parser).type == PN_TOK_COMMA) {
err = pni_parser_shift(parser);
if (err) return err;
} else {
break;
}
}
}
pn_data_exit(data);
if (pni_parser_token(parser).type == PN_TOK_RBRACKET) {
return pni_parser_shift(parser);
} else {
return pn_parser_err(parser, PN_ERR, "expecting ']'");
}
} else {
return pn_parser_err(parser, PN_ERR, "expecting '['");
}
}
static void pni_parser_append_tok(pn_parser_t *parser, char *dst, int *idx)
{
memcpy(dst + *idx, pni_parser_token(parser).start, pni_parser_token(parser).size);
*idx += pni_parser_token(parser).size;
}
static int pni_parser_number(pn_parser_t *parser, pn_data_t *data)
{
bool dbl = false;
char number[1024];
int idx = 0;
int err;
bool negate = false;
if (pni_parser_token(parser).type == PN_TOK_NEG || pni_parser_token(parser).type == PN_TOK_POS) {
if (pni_parser_token(parser).type == PN_TOK_NEG)
negate = !negate;
err = pni_parser_shift(parser);
if (err) return err;
}
if (pni_parser_token(parser).type == PN_TOK_FLOAT || pni_parser_token(parser).type == PN_TOK_INT) {
dbl = pni_parser_token(parser).type == PN_TOK_FLOAT;
pni_parser_append_tok(parser, number, &idx);
err = pni_parser_shift(parser);
if (err) return err;
} else {
return pn_parser_err(parser, PN_ERR, "expecting FLOAT or INT");
}
number[idx] = '\0';
if (dbl) {
double value = atof(number);
if (negate) {
value = -value;
}
err = pn_data_put_double(data, value);
if (err) return pn_parser_err(parser, err, "error writing double");
} else {
int64_t value = pn_i_atoll(number);
if (negate) {
value = -value;
}
err = pn_data_put_long(data, value);
if (err) return pn_parser_err(parser, err, "error writing long");
}
return 0;
}
static int pni_parser_unquote(pn_parser_t *parser, char *dst, const char *src, size_t *n)
{
size_t idx = 0;
bool escape = false;
int start, end;
if (src[0] != '"') {
if (src[1] == '"') {
start = 2;
end = *n - 1;
} else {
start = 1;
end = *n;
}
} else {
start = 1;
end = *n - 1;
}
for (int i = start; i < end; i++)
{
char c = src[i];
if (escape) {
switch (c) {
case '"':
case '\\':
case '/':
dst[idx++] = c;
escape = false;
break;
case 'b':
dst[idx++] = '\b';
break;
case 'f':
dst[idx++] = '\f';
break;
case 'n':
dst[idx++] = '\n';
break;
case 'r':
dst[idx++] = '\r';
break;
case 't':
dst[idx++] = '\t';
break;
case 'x':
{
char n1 = toupper(src[i+1]);
char n2 = n1 ? toupper(src[i+2]) : 0;
if (!n2) {
return pn_parser_err(parser, PN_ERR, "truncated escape code");
}
int d1 = isdigit(n1) ? n1 - '0' : n1 - 'A' + 10;
int d2 = isdigit(n2) ? n2 - '0' : n2 - 'A' + 10;
dst[idx++] = d1*16 + d2;
i += 2;
}
break;
// XXX: need to handle unicode escapes: 'u'
default:
return pn_parser_err(parser, PN_ERR, "unrecognized escape code");
}
escape = false;
} else {
switch (c)
{
case '\\':
escape = true;
break;
default:
dst[idx++] = c;
break;
}
}
}
dst[idx++] = '\0';
*n = idx;
return 0;
}
static int pni_parser_value(pn_parser_t *parser, pn_data_t *data)
{
int err;
size_t n;
char *dst;
pn_token_t tok = pni_parser_token(parser);
switch (tok.type)
{
case PN_TOK_AT:
return pni_parser_descriptor(parser, data);
case PN_TOK_LBRACE:
return pni_parser_map(parser, data);
case PN_TOK_LBRACKET:
return pni_parser_list(parser, data);
case PN_TOK_BINARY:
case PN_TOK_SYMBOL:
case PN_TOK_STRING:
n = tok.size;
pni_parser_ensure(parser, n);
dst = parser->atoms + parser->size;
err = pni_parser_unquote(parser, dst, tok.start, &n);
if (err) return err;
parser->size += n;
switch (tok.type) {
case PN_TOK_BINARY:
err = pn_data_put_binary(data, pn_bytes(n - 1, dst));
break;
case PN_TOK_STRING:
err = pn_data_put_string(data, pn_bytes(n - 1, dst));
break;
case PN_TOK_SYMBOL:
err = pn_data_put_symbol(data, pn_bytes(n - 1, dst));
break;
default:
return pn_parser_err(parser, PN_ERR, "internal error");
}
if (err) return pn_parser_err(parser, err, "error writing string/binary/symbol");
return pni_parser_shift(parser);
case PN_TOK_POS:
case PN_TOK_NEG:
case PN_TOK_FLOAT:
case PN_TOK_INT:
return pni_parser_number(parser, data);
case PN_TOK_TRUE:
err = pn_data_put_bool(data, true);
if (err) return pn_parser_err(parser, err, "error writing boolean");
return pni_parser_shift(parser);
case PN_TOK_FALSE:
err = pn_data_put_bool(data, false);
if (err) return pn_parser_err(parser, err, "error writing boolean");
return pni_parser_shift(parser);
case PN_TOK_NULL:
err = pn_data_put_null(data);
if (err) return pn_parser_err(parser, err, "error writing null");
return pni_parser_shift(parser);
default:
return pn_parser_err(parser, PN_ERR, "expecting one of '[', '{', STRING, "
"SYMBOL, BINARY, true, false, null, NUMBER");
}
}
static int pni_parser_parse_r(pn_parser_t *parser, pn_data_t *data)
{
while (true) {
int err;
switch (pni_parser_token(parser).type)
{
case PN_TOK_EOS:
return 0;
case PN_TOK_ERR:
return PN_ERR;
default:
err = pni_parser_value(parser, data);
if (err) return err;
}
}
}
int pn_parser_parse(pn_parser_t *parser, const char *str, pn_data_t *data)
{
int err = pn_scanner_start(parser->scanner, str);
if (err) return err;
parser->size = 0;
return pni_parser_parse_r(parser, data);
}