diff --git a/examples/json.noja b/examples/json.noja new file mode 100644 index 0000000..f3ad389 --- /dev/null +++ b/examples/json.noja @@ -0,0 +1,78 @@ + +fun is_space(c) + return c == ' ' + or c == '\t' + or c == '\n'; + +fun is_digit(c) + return unicode(c) <= unicode('9') + and unicode(c) >= unicode('0'); + +fun skip(ctx, test) + while not ended(ctx) and test(current(ctx)): + next(ctx); + +fun ended(ctx) + return ctx.i == count(ctx.str); + +fun next(ctx) { + assert(ctx.i < count(ctx.str)); + ctx.i = ctx.i + 1; +} + +fun current(ctx) + return ctx.str[ctx.i]; + +fun parse(str) { + + if str == none: + str = ''; + + ctx = {str: str, i: 0}; + + skip(ctx, is_space); + + if ended(ctx): + # JSON source string only contains whitespace. + return none; + + return parse_any_value(ctx); +} + +fun parse_any_value(ctx) { + + if is_digit(current(ctx)): { + # Number (int or float) + parsed = 0; + while not ended(ctx) and is_digit(current(ctx)): { + parsed = parsed * 10 + unicode(current(ctx)) - unicode('0'); + next(ctx); + } + + if current(ctx) == '.': { + + next(ctx); + + if ended(ctx): + # Source string ended unexpectedly after dot. + return none; + + if not is_digit(current(ctx)): + # Got something other than a digit after dot. + return none; + + fact = 1; + while not ended(ctx) and is_digit(current(ctx)): { + fact = fact / 10.0; + parsed = parsed + fact * (unicode(current(ctx)) - unicode('0')); + next(ctx); + } + } + + return parsed; + } + + assert(false); +} + +parse(); \ No newline at end of file diff --git a/src/builtins/basic.c b/src/builtins/basic.c index 95aa3c6..d082445 100644 --- a/src/builtins/basic.c +++ b/src/builtins/basic.c @@ -187,7 +187,7 @@ static Object *bin_strcat(Runtime *runtime, Object **argv, unsigned int argc, Er { Error_Report(error, 1, "No memory"); return NULL; - } + } } Object *result = NULL; diff --git a/src/compiler/parse.c b/src/compiler/parse.c index b52a3a8..867e0e1 100644 --- a/src/compiler/parse.c +++ b/src/compiler/parse.c @@ -131,6 +131,7 @@ static Node *parse_ifelse_statement(Context *ctx); static Node *parse_compound_statement(Context *ctx, TokenKind end); static Node *parse_function_definition(Context *ctx); static Node *parse_postfix_expression(Context *ctx); +static Node *parse_prefix_expression(Context *ctx); static Node *parse_while_statement(Context *ctx); static Node *parse_dowhile_statement(Context *ctx); @@ -865,7 +866,6 @@ static Node *parse_list_primary_expression(Context *ctx) } - static Node *parse_map_primary_expression(Context *ctx) { assert(ctx != NULL); @@ -1067,42 +1067,6 @@ static Node *parse_primary_expresion(Context *ctx) switch(current(ctx)) { - case '+': - case '-': - case TKWNOT: - { - Token *unary_operator = current_token(ctx); - - next(ctx); - - Node *operand = parse_primary_expresion(ctx); - - if(operand == NULL) - return NULL; - - OperExprNode *temp = BPAlloc_Malloc(ctx->alloc, sizeof(OperExprNode)); - { - if(temp == NULL) - return NULL; - - temp->base.base.kind = NODE_EXPR; - temp->base.base.next = NULL; - temp->base.base.offset = unary_operator->offset; - temp->base.base.length = operand->offset + operand->length - unary_operator->offset; - temp->head = operand; - temp->count = 1; - - switch(unary_operator->kind) - { - case '+': temp->base.kind = EXPR_POS; break; - case '-': temp->base.kind = EXPR_NEG; break; - case TKWNOT: temp->base.kind = EXPR_NOT; break; - default: assert(0); break; - } - } - return (Node*) temp; - } - case '[': return parse_list_primary_expression(ctx); @@ -1504,6 +1468,64 @@ done: return node; } +static Node *parse_prefix_expression(Context *ctx) +{ + if(done(ctx)) + { + Error_Report(ctx->error, 0, "Source ended where a prefix expression was expected"); + return NULL; + } + + switch(current(ctx)) + { + case TDONE: + Error_Report(ctx->error, 1, "Unexpected end of source where a prefix expression was expected"); + return NULL; + + case '+': + case '-': + case TKWNOT: + { + Token *unary_operator = current_token(ctx); + + next(ctx); + + Node *operand = parse_prefix_expression(ctx); + + if(operand == NULL) + return NULL; + + OperExprNode *temp = BPAlloc_Malloc(ctx->alloc, sizeof(OperExprNode)); + { + if(temp == NULL) + return NULL; + + temp->base.base.kind = NODE_EXPR; + temp->base.base.next = NULL; + temp->base.base.offset = unary_operator->offset; + temp->base.base.length = operand->offset + operand->length - unary_operator->offset; + temp->head = operand; + temp->count = 1; + + switch(unary_operator->kind) + { + case '+': temp->base.kind = EXPR_POS; break; + case '-': temp->base.kind = EXPR_NEG; break; + case TKWNOT: temp->base.kind = EXPR_NOT; break; + default: assert(0); break; + } + } + return (Node*) temp; + } + + default: + return parse_postfix_expression(ctx); + } + + UNREACHABLE; + return NULL; +} + static inline _Bool isbinop(Token *tok) { assert(tok != NULL); @@ -1577,7 +1599,7 @@ static Node *parse_expression_2(Context *ctx, Node *left_expr, int min_prec) next(ctx); - Node *right_expr = parse_postfix_expression(ctx); + Node *right_expr = parse_prefix_expression(ctx); if(right_expr == NULL) return NULL; @@ -1640,7 +1662,7 @@ static Node *parse_expression_2(Context *ctx, Node *left_expr, int min_prec) static Node *parse_expression(Context *ctx) { - Node *left_expr = parse_postfix_expression(ctx); + Node *left_expr = parse_prefix_expression(ctx); if(left_expr == NULL) return NULL; diff --git a/src/objects/o_string.c b/src/objects/o_string.c index aa4131c..fb843a8 100644 --- a/src/objects/o_string.c +++ b/src/objects/o_string.c @@ -49,6 +49,7 @@ static void print(Object *obj, FILE *fp); static char *to_string(Object *self, int *size, Heap *heap, Error *err); static _Bool op_eql(Object *self, Object *other); static void walkexts(Object *self, void (*callback)(void **referer, unsigned int size, void *userp), void *userp); +static Object *select(Object *self, Object *key, Heap *heap, Error *error); static TypeObject t_string = { .base = (Object) { .type = &t_type, .flags = Object_STATIC }, @@ -59,11 +60,61 @@ static TypeObject t_string = { .count = count, .copy = copy, .print = print, + .select = select, .to_string = to_string, .op_eql = op_eql, .walkexts = walkexts, }; +static int char_index_to_offset(StringObject *str, int idx) +{ + // Iterate over a string to find the first byte of + // the utf-8 character number [idx]. + + int scanned_bytes = 0, + last_code_len = 0; + + while(idx > 0) + { + last_code_len = utf8_sequence_to_utf32_codepoint(str->body + scanned_bytes, str->bytes - scanned_bytes, NULL); + scanned_bytes += last_code_len; + idx -= 1; + + assert(scanned_bytes <= str->bytes); + } + + assert(idx == 0); + return scanned_bytes; +} + +static Object *select(Object *self, Object *key, Heap *heap, Error *error) +{ + assert(self != NULL && self->type == &t_string); + assert(key != NULL && heap != NULL && error != NULL); + + if(!Object_IsInt(key)) + { + Error_Report(error, 0, "Non integer key"); + return NULL; + } + + int idx = Object_ToInt(key, error); + assert(error->occurred == 0); + + StringObject *str = (StringObject*) self; + + if(idx < 0 || idx >= str->count) + { + Error_Report(error, 0, "Out of range index"); + return NULL; + } + + int byteoffset = char_index_to_offset(str, idx); + int codelength = utf8_sequence_to_utf32_codepoint(str->body + byteoffset, str->bytes - byteoffset, NULL); + + return Object_FromString(str->body + byteoffset, codelength, heap, error); +} + static char *to_string(Object *self, int *size, Heap *heap, Error *err) { assert(self != NULL);