2017-09-09 18:39:51 +00:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2017 Calvin Rose
|
|
|
|
*
|
|
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
|
|
* of this software and associated documentation files (the "Software"), to
|
|
|
|
* deal in the Software without restriction, including without limitation the
|
|
|
|
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
|
|
|
* sell copies of the Software, and to permit persons to whom the Software is
|
|
|
|
* furnished to do so, subject to the following conditions:
|
|
|
|
*
|
|
|
|
* The above copyright notice and this permission notice shall be included in
|
|
|
|
* all copies or substantial portions of the Software.
|
|
|
|
*
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
|
|
* IN THE SOFTWARE.
|
|
|
|
*/
|
|
|
|
|
2017-11-06 03:05:47 +00:00
|
|
|
#include <dst/dst.h>
|
2017-12-08 20:57:02 +00:00
|
|
|
#include "strtod.h"
|
2017-09-09 18:39:51 +00:00
|
|
|
|
|
|
|
/* Checks if a string slice is equal to a string constant */
|
|
|
|
static int check_str_const(const char *ref, const uint8_t *start, const uint8_t *end) {
|
|
|
|
while (*ref && start < end) {
|
|
|
|
if (*ref != *(char *)start) return 0;
|
|
|
|
++ref;
|
|
|
|
++start;
|
|
|
|
}
|
|
|
|
return !*ref && start == end;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Quote a value */
|
2017-11-06 03:05:47 +00:00
|
|
|
static DstValue quote(DstValue x) {
|
|
|
|
DstValue *t = dst_tuple_begin(2);
|
2017-11-27 19:03:34 +00:00
|
|
|
t[0] = dst_csymbolv("quote");
|
2017-11-06 03:05:47 +00:00
|
|
|
t[1] = x;
|
|
|
|
return dst_wrap_tuple(dst_tuple_end(t));
|
2017-09-09 18:39:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Check if a character is whitespace */
|
|
|
|
static int is_whitespace(uint8_t c) {
|
2017-11-01 21:53:43 +00:00
|
|
|
return c == ' '
|
|
|
|
|| c == '\t'
|
|
|
|
|| c == '\n'
|
|
|
|
|| c == '\r'
|
|
|
|
|| c == '\0'
|
|
|
|
|| c == ',';
|
2017-09-09 18:39:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Check if a character is a valid symbol character */
|
2017-12-03 17:52:09 +00:00
|
|
|
/* TODO - allow utf8 - shouldn't be difficult, err on side
|
2017-11-01 21:53:43 +00:00
|
|
|
* of inclusivity */
|
2017-09-09 18:39:51 +00:00
|
|
|
static int is_symbol_char(uint8_t c) {
|
|
|
|
if (c >= 'a' && c <= 'z') return 1;
|
|
|
|
if (c >= 'A' && c <= 'Z') return 1;
|
|
|
|
if (c >= '0' && c <= ':') return 1;
|
|
|
|
if (c >= '<' && c <= '@') return 1;
|
|
|
|
if (c >= '*' && c <= '/') return 1;
|
2017-11-25 04:17:04 +00:00
|
|
|
if (c >= '$' && c <= '&') return 1;
|
2017-09-09 18:39:51 +00:00
|
|
|
if (c == '_') return 1;
|
|
|
|
if (c == '^') return 1;
|
|
|
|
if (c == '!') return 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Get hex digit from a letter */
|
|
|
|
static int to_hex(uint8_t c) {
|
|
|
|
if (c >= '0' && c <= '9') {
|
|
|
|
return c - '0';
|
|
|
|
} else if (c >= 'A' && c <= 'F') {
|
|
|
|
return 10 + c - 'A';
|
2017-11-21 02:39:44 +00:00
|
|
|
} else if (c >= 'a' && c <= 'f') {
|
|
|
|
return 10 + c - 'a';
|
2017-09-09 18:39:51 +00:00
|
|
|
} else {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-03 17:52:09 +00:00
|
|
|
/* Make source mapping for atom (non recursive structure) */
|
|
|
|
static DstValue atom_map(int32_t start, int32_t end) {
|
|
|
|
DstValue *t = dst_tuple_begin(2);
|
|
|
|
t[0] = dst_wrap_integer(start);
|
|
|
|
t[1] = dst_wrap_integer(end);
|
|
|
|
return dst_wrap_tuple(dst_tuple_end(t));
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Create mappingd for recursive data structure */
|
|
|
|
static DstValue ds_map(int32_t start, int32_t end, DstValue submap) {
|
|
|
|
DstValue *t = dst_tuple_begin(3);
|
|
|
|
t[0] = dst_wrap_integer(start);
|
|
|
|
t[1] = dst_wrap_integer(end);
|
|
|
|
t[2] = submap;
|
|
|
|
return dst_wrap_tuple(dst_tuple_end(t));
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Create a sourcemapping for a key value pair */
|
|
|
|
static DstValue kv_map(DstValue k, DstValue v) {
|
|
|
|
DstValue *t = dst_tuple_begin(2);
|
|
|
|
t[0] = k;
|
|
|
|
t[1] = v;
|
|
|
|
return dst_wrap_tuple(dst_tuple_end(t));
|
|
|
|
}
|
|
|
|
|
2017-09-09 18:39:51 +00:00
|
|
|
typedef struct {
|
2017-11-21 02:39:44 +00:00
|
|
|
DstArray stack;
|
2017-12-03 17:52:09 +00:00
|
|
|
DstArray mapstack;
|
|
|
|
const uint8_t *srcstart;
|
2017-09-09 18:39:51 +00:00
|
|
|
const uint8_t *end;
|
2017-11-01 21:53:43 +00:00
|
|
|
const char *errmsg;
|
2017-11-21 02:39:44 +00:00
|
|
|
DstParseStatus status;
|
2017-09-09 18:39:51 +00:00
|
|
|
} ParseArgs;
|
|
|
|
|
2017-11-01 21:53:43 +00:00
|
|
|
/* Entry point of the recursive descent parser */
|
|
|
|
static const uint8_t *parse_recur(
|
2017-09-09 18:39:51 +00:00
|
|
|
ParseArgs *args,
|
|
|
|
const uint8_t *src,
|
2017-11-28 23:27:55 +00:00
|
|
|
int32_t recur) {
|
2017-09-09 18:39:51 +00:00
|
|
|
|
|
|
|
const uint8_t *end = args->end;
|
2017-12-03 17:52:09 +00:00
|
|
|
const uint8_t *mapstart;
|
2017-11-28 23:27:55 +00:00
|
|
|
int32_t qcount = 0;
|
2017-11-06 03:05:47 +00:00
|
|
|
DstValue ret;
|
2017-12-03 17:52:09 +00:00
|
|
|
DstValue submapping;
|
2017-09-09 18:39:51 +00:00
|
|
|
|
|
|
|
/* Prevent stack overflow */
|
|
|
|
if (recur == 0) goto too_much_recur;
|
|
|
|
|
2017-11-21 02:39:44 +00:00
|
|
|
/* try parsing again */
|
|
|
|
begin:
|
|
|
|
|
2017-09-09 18:39:51 +00:00
|
|
|
/* Trim leading whitespace and count quotes */
|
|
|
|
while (src < end && (is_whitespace(*src) || *src == '\'')) {
|
|
|
|
if (*src == '\'') {
|
|
|
|
++qcount;
|
|
|
|
}
|
|
|
|
++src;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check for end of source */
|
|
|
|
if (src >= end) goto unexpected_eos;
|
2017-12-03 17:52:09 +00:00
|
|
|
|
|
|
|
/* Open mapping */
|
|
|
|
mapstart = src;
|
|
|
|
submapping = dst_wrap_nil();
|
2017-09-09 18:39:51 +00:00
|
|
|
|
|
|
|
/* Detect token type based on first character */
|
|
|
|
switch (*src) {
|
|
|
|
|
|
|
|
/* Numbers, symbols, simple literals */
|
2017-11-21 02:39:44 +00:00
|
|
|
default:
|
|
|
|
atom:
|
|
|
|
{
|
2017-12-08 20:57:02 +00:00
|
|
|
DstValue numcheck;
|
2017-09-09 18:39:51 +00:00
|
|
|
const uint8_t *tokenend = src;
|
|
|
|
if (!is_symbol_char(*src)) goto unexpected_character;
|
|
|
|
while (tokenend < end && is_symbol_char(*tokenend))
|
|
|
|
tokenend++;
|
2017-12-08 20:57:02 +00:00
|
|
|
numcheck = dst_scan_number(src, tokenend - src);
|
|
|
|
if (!dst_checktype(numcheck, DST_NIL)) {
|
|
|
|
ret = numcheck;
|
2017-09-09 18:39:51 +00:00
|
|
|
} else if (check_str_const("nil", src, tokenend)) {
|
2017-11-06 03:05:47 +00:00
|
|
|
ret = dst_wrap_nil();
|
2017-09-09 18:39:51 +00:00
|
|
|
} else if (check_str_const("false", src, tokenend)) {
|
2017-11-06 03:05:47 +00:00
|
|
|
ret = dst_wrap_boolean(0);
|
2017-09-09 18:39:51 +00:00
|
|
|
} else if (check_str_const("true", src, tokenend)) {
|
2017-11-06 03:05:47 +00:00
|
|
|
ret = dst_wrap_boolean(1);
|
2017-09-09 18:39:51 +00:00
|
|
|
} else {
|
|
|
|
if (*src >= '0' && *src <= '9') {
|
|
|
|
goto sym_nodigits;
|
|
|
|
} else {
|
2017-11-27 19:03:34 +00:00
|
|
|
ret = dst_symbolv(src, tokenend - src);
|
2017-09-09 18:39:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
src = tokenend;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2017-11-21 02:39:44 +00:00
|
|
|
case '#':
|
|
|
|
{
|
|
|
|
/* Jump to next newline */
|
|
|
|
while (src < end && *src != '\n')
|
|
|
|
++src;
|
|
|
|
goto begin;
|
|
|
|
}
|
|
|
|
|
2017-09-09 18:39:51 +00:00
|
|
|
/* String literals */
|
2017-11-21 02:39:44 +00:00
|
|
|
case '"':
|
|
|
|
{
|
2017-09-09 18:39:51 +00:00
|
|
|
const uint8_t *strend = ++src;
|
2017-11-21 02:39:44 +00:00
|
|
|
const uint8_t *strstart = strend;
|
2017-11-28 23:27:55 +00:00
|
|
|
int32_t len = 0;
|
2017-09-09 18:39:51 +00:00
|
|
|
int containsEscape = 0;
|
|
|
|
/* Preprocess string to check for escapes and string end */
|
|
|
|
while (strend < end && *strend != '"') {
|
|
|
|
len++;
|
|
|
|
if (*strend++ == '\\') {
|
2017-11-01 21:53:43 +00:00
|
|
|
containsEscape = 1;
|
2017-09-09 18:39:51 +00:00
|
|
|
if (strend >= end) goto unexpected_eos;
|
|
|
|
if (*strend == 'h') {
|
2017-11-21 02:39:44 +00:00
|
|
|
strend += 3;
|
2017-09-09 18:39:51 +00:00
|
|
|
if (strend >= end) goto unexpected_eos;
|
2017-11-01 21:53:43 +00:00
|
|
|
} else {
|
|
|
|
strend++;
|
2017-11-21 02:39:44 +00:00
|
|
|
if (strend >= end) goto unexpected_eos;
|
2017-09-09 18:39:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (containsEscape) {
|
2017-11-06 03:05:47 +00:00
|
|
|
uint8_t *buf = dst_string_begin(len);
|
2017-09-09 18:39:51 +00:00
|
|
|
uint8_t *write = buf;
|
2017-11-01 21:53:43 +00:00
|
|
|
while (src < strend) {
|
|
|
|
if (*src == '\\') {
|
|
|
|
src++;
|
2017-11-21 02:39:44 +00:00
|
|
|
switch (*src++) {
|
2017-09-09 18:39:51 +00:00
|
|
|
case 'n': *write++ = '\n'; break;
|
|
|
|
case 'r': *write++ = '\r'; break;
|
|
|
|
case 't': *write++ = '\t'; break;
|
|
|
|
case 'f': *write++ = '\f'; break;
|
|
|
|
case '0': *write++ = '\0'; break;
|
|
|
|
case '"': *write++ = '"'; break;
|
|
|
|
case '\'': *write++ = '\''; break;
|
|
|
|
case 'z': *write++ = '\0'; break;
|
|
|
|
case 'e': *write++ = 27; break;
|
|
|
|
case 'h': {
|
2017-11-01 21:53:43 +00:00
|
|
|
int d1 = to_hex(*src++);
|
|
|
|
int d2 = to_hex(*src++);
|
2017-09-09 18:39:51 +00:00
|
|
|
if (d1 < 0 || d2 < 0) goto invalid_hex;
|
2017-11-01 21:53:43 +00:00
|
|
|
*write++ = 16 * d1 + d2;
|
2017-09-09 18:39:51 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
goto unknown_strescape;
|
|
|
|
}
|
|
|
|
} else {
|
2017-11-01 21:53:43 +00:00
|
|
|
*write++ = *src++;
|
2017-09-09 18:39:51 +00:00
|
|
|
}
|
|
|
|
}
|
2017-11-06 03:05:47 +00:00
|
|
|
ret = dst_wrap_string(dst_string_end(buf));
|
2017-09-09 18:39:51 +00:00
|
|
|
} else {
|
2017-11-06 03:05:47 +00:00
|
|
|
ret = dst_wrap_string(dst_string(strstart, strend - strstart));
|
2017-09-09 18:39:51 +00:00
|
|
|
}
|
2017-11-21 02:39:44 +00:00
|
|
|
src = strend + 1;
|
2017-09-09 18:39:51 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Data Structure literals */
|
2017-11-21 02:39:44 +00:00
|
|
|
case '@':
|
|
|
|
if (src[1] != '{')
|
|
|
|
goto atom;
|
2017-11-01 21:53:43 +00:00
|
|
|
case '(':
|
|
|
|
case '[':
|
2017-11-21 02:39:44 +00:00
|
|
|
case '{':
|
|
|
|
{
|
2017-11-28 23:27:55 +00:00
|
|
|
int32_t n = 0, i = 0;
|
|
|
|
int32_t istable = 0;
|
2017-09-09 18:39:51 +00:00
|
|
|
uint8_t close;
|
|
|
|
switch (*src++) {
|
|
|
|
case '[': close = ']'; break;
|
|
|
|
case '{': close = '}'; break;
|
2017-11-21 02:39:44 +00:00
|
|
|
case '@': close = '}'; src++; istable = 1; break;
|
2017-09-09 18:39:51 +00:00
|
|
|
default: close = ')'; break;
|
|
|
|
}
|
2017-11-21 02:39:44 +00:00
|
|
|
/* Trim trailing whitespace */
|
|
|
|
while (src < end && (is_whitespace(*src)))
|
|
|
|
++src;
|
2017-09-09 18:39:51 +00:00
|
|
|
/* Recursively parse inside literal */
|
|
|
|
while (*src != close) {
|
2017-11-01 21:53:43 +00:00
|
|
|
src = parse_recur(args, src, recur - 1);
|
|
|
|
if (args->errmsg || !src) return src;
|
|
|
|
n++;
|
|
|
|
/* Trim trailing whitespace */
|
|
|
|
while (src < end && (is_whitespace(*src)))
|
|
|
|
++src;
|
2017-09-09 18:39:51 +00:00
|
|
|
}
|
|
|
|
src++;
|
|
|
|
switch (close) {
|
|
|
|
case ')':
|
2017-11-06 03:05:47 +00:00
|
|
|
{
|
|
|
|
DstValue *tup = dst_tuple_begin(n);
|
2017-12-03 17:52:09 +00:00
|
|
|
DstValue *subtup = dst_tuple_begin(n);
|
|
|
|
for (i = n; i > 0; i--) {
|
2017-11-21 02:39:44 +00:00
|
|
|
tup[i - 1] = dst_array_pop(&args->stack);
|
2017-12-03 17:52:09 +00:00
|
|
|
subtup[i - 1] = dst_array_pop(&args->mapstack);
|
|
|
|
}
|
2017-11-06 03:05:47 +00:00
|
|
|
ret = dst_wrap_tuple(dst_tuple_end(tup));
|
2017-12-03 17:52:09 +00:00
|
|
|
submapping = dst_wrap_tuple(dst_tuple_end(subtup));
|
2017-09-09 18:39:51 +00:00
|
|
|
break;
|
2017-11-06 03:05:47 +00:00
|
|
|
}
|
2017-11-21 02:39:44 +00:00
|
|
|
case ']':
|
|
|
|
{
|
|
|
|
DstArray *arr = dst_array(n);
|
2017-12-03 17:52:09 +00:00
|
|
|
DstArray *subarr = dst_array(n);
|
|
|
|
for (i = n; i > 0; i--) {
|
2017-11-21 02:39:44 +00:00
|
|
|
arr->data[i - 1] = dst_array_pop(&args->stack);
|
2017-12-03 17:52:09 +00:00
|
|
|
subarr->data[i - 1] = dst_array_pop(&args->mapstack);
|
|
|
|
}
|
2017-11-21 02:39:44 +00:00
|
|
|
arr->count = n;
|
2017-12-03 17:52:09 +00:00
|
|
|
subarr->count = n;
|
2017-11-21 02:39:44 +00:00
|
|
|
ret = dst_wrap_array(arr);
|
2017-12-03 17:52:09 +00:00
|
|
|
submapping = dst_wrap_array(subarr);
|
2017-11-21 02:39:44 +00:00
|
|
|
break;
|
|
|
|
}
|
2017-09-09 18:39:51 +00:00
|
|
|
case '}':
|
2017-11-06 03:05:47 +00:00
|
|
|
{
|
2017-11-01 21:53:43 +00:00
|
|
|
if (n & 1) goto struct_oddargs;
|
2017-11-21 02:39:44 +00:00
|
|
|
if (istable) {
|
|
|
|
DstTable *t = dst_table(n);
|
2017-12-03 17:52:09 +00:00
|
|
|
DstTable *subt = dst_table(n);
|
2017-11-21 02:39:44 +00:00
|
|
|
for (i = n; i > 0; i -= 2) {
|
|
|
|
DstValue val = dst_array_pop(&args->stack);
|
|
|
|
DstValue key = dst_array_pop(&args->stack);
|
2017-12-03 17:52:09 +00:00
|
|
|
DstValue subval = dst_array_pop(&args->mapstack);
|
|
|
|
DstValue subkey = dst_array_pop(&args->mapstack);
|
|
|
|
|
2017-11-21 02:39:44 +00:00
|
|
|
dst_table_put(t, key, val);
|
2017-12-03 17:52:09 +00:00
|
|
|
dst_table_put(subt, key, kv_map(subkey, subval));
|
2017-11-21 02:39:44 +00:00
|
|
|
}
|
|
|
|
ret = dst_wrap_table(t);
|
2017-12-03 17:52:09 +00:00
|
|
|
submapping = dst_wrap_table(subt);
|
2017-11-21 02:39:44 +00:00
|
|
|
} else {
|
|
|
|
DstValue *st = dst_struct_begin(n >> 1);
|
2017-12-03 17:52:09 +00:00
|
|
|
DstValue *subst = dst_struct_begin(n >> 1);
|
2017-11-21 02:39:44 +00:00
|
|
|
for (i = n; i > 0; i -= 2) {
|
|
|
|
DstValue val = dst_array_pop(&args->stack);
|
|
|
|
DstValue key = dst_array_pop(&args->stack);
|
2017-12-03 17:52:09 +00:00
|
|
|
DstValue subval = dst_array_pop(&args->mapstack);
|
|
|
|
DstValue subkey = dst_array_pop(&args->mapstack);
|
|
|
|
|
2017-11-21 02:39:44 +00:00
|
|
|
dst_struct_put(st, key, val);
|
2017-12-03 17:52:09 +00:00
|
|
|
dst_struct_put(subst, key, kv_map(subkey, subval));
|
2017-11-21 02:39:44 +00:00
|
|
|
}
|
|
|
|
ret = dst_wrap_struct(dst_struct_end(st));
|
2017-12-03 17:52:09 +00:00
|
|
|
submapping = dst_wrap_struct(dst_struct_end(subst));
|
2017-11-06 03:05:47 +00:00
|
|
|
}
|
2017-09-09 18:39:51 +00:00
|
|
|
break;
|
2017-11-06 03:05:47 +00:00
|
|
|
}
|
2017-09-09 18:39:51 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Quote the returned value qcount times */
|
2017-11-06 03:05:47 +00:00
|
|
|
while (qcount--) ret = quote(ret);
|
|
|
|
|
|
|
|
/* Push the result to the stack */
|
2017-11-21 02:39:44 +00:00
|
|
|
dst_array_push(&args->stack, ret);
|
2017-12-03 17:52:09 +00:00
|
|
|
|
|
|
|
/* Push source mapping */
|
|
|
|
if (dst_checktype(submapping, DST_NIL)) {
|
|
|
|
/* We just parsed an atom */
|
|
|
|
dst_array_push(&args->mapstack, atom_map(
|
|
|
|
mapstart - args->srcstart,
|
|
|
|
src - args->srcstart));
|
|
|
|
} else {
|
|
|
|
/* We just parsed a recursive data structure */
|
|
|
|
dst_array_push(&args->mapstack, ds_map(
|
|
|
|
mapstart - args->srcstart,
|
|
|
|
src - args->srcstart,
|
|
|
|
submapping));
|
|
|
|
}
|
2017-09-09 18:39:51 +00:00
|
|
|
|
|
|
|
/* Return the new source position for further calls */
|
|
|
|
return src;
|
|
|
|
|
|
|
|
/* Errors below */
|
|
|
|
|
|
|
|
unexpected_eos:
|
2017-11-01 21:53:43 +00:00
|
|
|
args->errmsg = "unexpected end of source";
|
2017-11-06 03:05:47 +00:00
|
|
|
args->status = DST_PARSE_UNEXPECTED_EOS;
|
2017-09-09 18:39:51 +00:00
|
|
|
return NULL;
|
|
|
|
|
|
|
|
unexpected_character:
|
2017-11-01 21:53:43 +00:00
|
|
|
args->errmsg = "unexpected character";
|
2017-11-06 03:05:47 +00:00
|
|
|
args->status = DST_PARSE_ERROR;
|
2017-09-09 18:39:51 +00:00
|
|
|
return src;
|
|
|
|
|
|
|
|
sym_nodigits:
|
2017-11-01 21:53:43 +00:00
|
|
|
args->errmsg = "symbols cannot start with digits";
|
2017-11-06 03:05:47 +00:00
|
|
|
args->status = DST_PARSE_ERROR;
|
2017-09-09 18:39:51 +00:00
|
|
|
return src;
|
|
|
|
|
|
|
|
struct_oddargs:
|
2017-11-01 21:53:43 +00:00
|
|
|
args->errmsg = "struct literal needs an even number of arguments";
|
2017-11-06 03:05:47 +00:00
|
|
|
args->status = DST_PARSE_ERROR;
|
2017-09-09 18:39:51 +00:00
|
|
|
return src;
|
|
|
|
|
|
|
|
unknown_strescape:
|
2017-11-01 21:53:43 +00:00
|
|
|
args->errmsg = "unknown string escape sequence";
|
2017-11-06 03:05:47 +00:00
|
|
|
args->status = DST_PARSE_ERROR;
|
2017-09-09 18:39:51 +00:00
|
|
|
return src;
|
|
|
|
|
|
|
|
invalid_hex:
|
2017-11-01 21:53:43 +00:00
|
|
|
args->errmsg = "invalid hex escape in string";
|
2017-11-06 03:05:47 +00:00
|
|
|
args->status = DST_PARSE_ERROR;
|
2017-09-09 18:39:51 +00:00
|
|
|
return src;
|
|
|
|
|
|
|
|
too_much_recur:
|
2017-11-01 21:53:43 +00:00
|
|
|
args->errmsg = "recursed too deeply in parsing";
|
2017-11-06 03:05:47 +00:00
|
|
|
args->status = DST_PARSE_ERROR;
|
2017-09-09 18:39:51 +00:00
|
|
|
return src;
|
|
|
|
}
|
|
|
|
|
2017-11-06 03:05:47 +00:00
|
|
|
/* Parse an array of bytes. Return value in the fiber return value. */
|
2017-11-28 23:27:55 +00:00
|
|
|
DstParseResult dst_parse(const uint8_t *src, int32_t len) {
|
2017-11-21 02:39:44 +00:00
|
|
|
DstParseResult res;
|
2017-09-09 18:39:51 +00:00
|
|
|
ParseArgs args;
|
2017-11-21 02:39:44 +00:00
|
|
|
const uint8_t *newsrc;
|
2017-09-09 18:39:51 +00:00
|
|
|
|
2017-11-21 02:39:44 +00:00
|
|
|
dst_array_init(&args.stack, 10);
|
2017-11-06 03:05:47 +00:00
|
|
|
args.status = DST_PARSE_OK;
|
2017-12-03 17:52:09 +00:00
|
|
|
args.srcstart = src;
|
2017-09-09 18:39:51 +00:00
|
|
|
args.end = src + len;
|
|
|
|
args.errmsg = NULL;
|
2017-11-01 21:53:43 +00:00
|
|
|
|
2017-12-03 17:52:09 +00:00
|
|
|
dst_array_init(&args.mapstack, 10);
|
|
|
|
|
2017-11-21 02:39:44 +00:00
|
|
|
newsrc = parse_recur(&args, src, DST_RECURSION_GUARD);
|
|
|
|
res.status = args.status;
|
2017-11-28 23:27:55 +00:00
|
|
|
res.bytes_read = (int32_t) (newsrc - src);
|
2017-11-21 02:39:44 +00:00
|
|
|
|
2017-09-09 18:39:51 +00:00
|
|
|
if (args.errmsg) {
|
2017-11-21 02:39:44 +00:00
|
|
|
res.result.error = dst_cstring(args.errmsg);
|
2017-12-08 20:57:02 +00:00
|
|
|
res.map = NULL;
|
2017-11-06 03:05:47 +00:00
|
|
|
} else {
|
2017-11-21 02:39:44 +00:00
|
|
|
res.result.value = dst_array_pop(&args.stack);
|
2017-12-08 20:57:02 +00:00
|
|
|
res.map = dst_unwrap_tuple(dst_array_pop(&args.mapstack));
|
2017-09-09 18:39:51 +00:00
|
|
|
}
|
2017-11-01 21:53:43 +00:00
|
|
|
|
2017-11-21 02:39:44 +00:00
|
|
|
dst_array_deinit(&args.stack);
|
2017-12-03 17:52:09 +00:00
|
|
|
dst_array_deinit(&args.mapstack);
|
2017-11-06 03:05:47 +00:00
|
|
|
|
2017-11-21 02:39:44 +00:00
|
|
|
return res;
|
2017-09-09 18:39:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Parse a c string */
|
2017-11-21 02:39:44 +00:00
|
|
|
DstParseResult dst_parsec(const char *src) {
|
2017-11-28 23:27:55 +00:00
|
|
|
int32_t len = 0;
|
2017-09-09 18:39:51 +00:00
|
|
|
while (src[len]) ++len;
|
2017-11-21 02:39:44 +00:00
|
|
|
return dst_parse((const uint8_t *)src, len);
|
2017-09-09 18:39:51 +00:00
|
|
|
}
|
2017-12-08 20:57:02 +00:00
|
|
|
|
|
|
|
/* Get the sub source map by indexing a value. Used to traverse
|
|
|
|
* into arrays and tuples */
|
|
|
|
const DstValue *dst_parse_submap_index(const DstValue *map, int32_t index) {
|
|
|
|
if (NULL != map && dst_tuple_length(map) >= 3) {
|
|
|
|
const DstValue *seq;
|
|
|
|
int32_t len;
|
|
|
|
if (dst_seq_view(map[2], &seq, &len)) {
|
|
|
|
if (index >= 0 && index < len) {
|
|
|
|
if (dst_checktype(seq[index], DST_TUPLE)) {
|
|
|
|
const DstValue *ret = dst_unwrap_tuple(seq[index]);
|
|
|
|
if (dst_tuple_length(ret) >= 2 &&
|
|
|
|
dst_checktype(ret[0], DST_INTEGER) &&
|
|
|
|
dst_checktype(ret[1], DST_INTEGER)) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Traverse into tables and structs */
|
|
|
|
static const DstValue *dst_parse_submap_kv(const DstValue *map, DstValue key, int kv) {
|
|
|
|
if (NULL != map && dst_tuple_length(map) >= 3) {
|
|
|
|
DstValue kvpair = dst_get(map[2], key);
|
|
|
|
if (dst_checktype(kvpair, DST_TUPLE)) {
|
|
|
|
const DstValue *kvtup = dst_unwrap_tuple(kvpair);
|
|
|
|
if (dst_tuple_length(kvtup) >= 2) {
|
|
|
|
if (dst_checktype(kvtup[kv], DST_TUPLE)) {
|
|
|
|
const DstValue *ret = dst_unwrap_tuple(kvtup[kv]);
|
|
|
|
if (dst_tuple_length(ret) >= 2 &&
|
|
|
|
dst_checktype(ret[0], DST_INTEGER) &&
|
|
|
|
dst_checktype(ret[1], DST_INTEGER)) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Traverse into a key of a table or struct */
|
|
|
|
const DstValue *dst_parse_submap_key(const DstValue *map, DstValue key) {
|
|
|
|
return dst_parse_submap_kv(map, key, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Traverse into a value of a table or struct */
|
|
|
|
const DstValue *dst_parse_submap_value(const DstValue *map, DstValue key) {
|
|
|
|
return dst_parse_submap_kv(map, key, 1);
|
|
|
|
}
|