string arithmetic.

main
bog 2024-03-19 14:23:11 +01:00
parent fe0767bfe5
commit f0fa844c8a
14 changed files with 285 additions and 40 deletions

View File

@ -17,7 +17,7 @@ LITERAL ::=
| INDEX | INDEX
| opar EXPR cpar | opar EXPR cpar
INDEX ::= INDEX ::=
| TUPLE osquare (EXPR (comma EXPR)*)? csquare | (TUPLE|str) osquare (EXPR (comma EXPR)*)? csquare
TUPLE ::= TUPLE ::=
| opar EXPR+ cpar | opar EXPR+ cpar
BUILTIN ::= num | bool BUILTIN ::= num | bool | str

View File

@ -1,4 +1,5 @@
#include "ccm.h" #include "ccm.h"
#include "str.h"
void ccm_init(ccm_t* self) void ccm_init(ccm_t* self)
{ {
@ -45,6 +46,10 @@ CCM ccm_from_value(ccm_t* self, value_t* value)
return ccm_to_num(self, value->data.num, value->line); return ccm_to_num(self, value->data.num, value->line);
} break; } break;
case TYPE_STR: {
return ccm_to_str(self, value->data.str, value->line);
} break;
case TYPE_BOOLEAN: { case TYPE_BOOLEAN: {
return ccm_to_boolean( return ccm_to_boolean(
self, self,
@ -165,6 +170,31 @@ CCM ccm_to_boolean(ccm_t* self, int value, int line)
return self->values.size - 1; return self->values.size - 1;
} }
int ccm_is_str(ccm_t* self, CCM value)
{
assert(self);
return ((value_t*) self->values.data[value])->type == TYPE_STR;
}
char* ccm_from_str(ccm_t* self, CCM value)
{
assert(self);
return ((value_t*) self->values.data[value])->data.str;
}
CCM ccm_to_str(ccm_t* self, char* value, int line)
{
assert(self);
assert(value);
value_t* val = malloc(sizeof(value_t));
value_init_str(val, value, line);
vec_push(&self->values, val);
return self->values.size - 1;
}
void ccm_push(ccm_t* self, CCM value) void ccm_push(ccm_t* self, CCM value)
{ {
assert(self); assert(self);
@ -223,6 +253,21 @@ void ccm_add(ccm_t* self)
CCM ccm_lhs = ccm_pop(self); CCM ccm_lhs = ccm_pop(self);
int line = ((value_t*) self->values.data[ccm_lhs])->line; int line = ((value_t*) self->values.data[ccm_lhs])->line;
if (ccm_is_str(self, ccm_rhs)
&& ccm_is_str(self, ccm_lhs))
{
char const* lhs = ccm_from_str(self, ccm_lhs);
char const* rhs = ccm_from_str(self, ccm_rhs);
str_t s;
str_init(&s);
str_push_cstr(&s, lhs);
str_push_cstr(&s, rhs);
ccm_push(self, ccm_to_str(self, s.value, line));
str_free(&s);
return;
}
double rhs = ccm_from_num(self, ccm_rhs); double rhs = ccm_from_num(self, ccm_rhs);
double lhs = ccm_from_num(self, ccm_lhs); double lhs = ccm_from_num(self, ccm_lhs);
@ -260,6 +305,32 @@ void ccm_mul(ccm_t* self)
CCM ccm_lhs = ccm_pop(self); CCM ccm_lhs = ccm_pop(self);
int line = ((value_t*) self->values.data[ccm_lhs])->line; int line = ((value_t*) self->values.data[ccm_lhs])->line;
if (ccm_is_str(self, ccm_rhs)
&& ccm_is_num(self, ccm_lhs))
{
CCM tmp = ccm_lhs;
ccm_lhs = ccm_rhs;
ccm_rhs = tmp;
}
if (ccm_is_str(self, ccm_lhs)
&& ccm_is_num(self, ccm_rhs))
{
int count = ccm_from_num(self, ccm_rhs);
char const* val = ccm_from_str(self, ccm_lhs);
str_t s;
str_init(&s);
for (int i=0; i<count; i++)
{
str_push_cstr(&s, val);
}
ccm_push(self, ccm_to_str(self, s.value, line));
str_free(&s);
return;
}
double rhs = ccm_from_num(self, ccm_rhs); double rhs = ccm_from_num(self, ccm_rhs);
double lhs = ccm_from_num(self, ccm_lhs); double lhs = ccm_from_num(self, ccm_lhs);

View File

@ -33,6 +33,10 @@ int ccm_is_boolean(ccm_t* self, CCM value);
int ccm_from_boolean(ccm_t* self, CCM value); int ccm_from_boolean(ccm_t* self, CCM value);
CCM ccm_to_boolean(ccm_t* self, int value, int line); CCM ccm_to_boolean(ccm_t* self, int value, int line);
int ccm_is_str(ccm_t* self, CCM value);
char* ccm_from_str(ccm_t* self, CCM value);
CCM ccm_to_str(ccm_t* self, char* value, int line);
void ccm_push(ccm_t* self, CCM value); void ccm_push(ccm_t* self, CCM value);
CCM ccm_pop(ccm_t* self); CCM ccm_pop(ccm_t* self);
CCM ccm_top(ccm_t* self, int depth); CCM ccm_top(ccm_t* self, int depth);

View File

@ -77,6 +77,17 @@ void compiler_compile(compiler_t* self,
} }
} break; } break;
case NODE_STR: {
size_t id = prog_add_new_constant(
prog,
ccm_to_str(&self->module->ccm,
node->value,
node->line)
);
prog_add_instr(prog, OP_PUSH, id);
} break;
case NODE_NUM: { case NODE_NUM: {
size_t id = prog_add_new_constant( size_t id = prog_add_new_constant(
prog, prog,

View File

@ -46,9 +46,50 @@ void exec_instr(exec_t* self,
{ {
case OP_INDEX: { case OP_INDEX: {
CCM ccm_target = ccm_pop(ccm); CCM ccm_target = ccm_pop(ccm);
vec_t* target = ccm_from_tuple(ccm, ccm_target);
value_t* result = NULL; value_t* result = NULL;
if (ccm_is_str(ccm, ccm_target))
{
int line =
((value_t*)ccm->values.data[ccm_target])->line
;
if (param != 1)
{
err_push(&self->err, line,
"index out of bounds");
return;
}
char const* target =
ccm_from_str(ccm, ccm_target);
size_t size = strlen(target);
CCM ccm_idx = ccm_pop(ccm);
int idx = ccm_from_num(ccm, ccm_idx);
if (idx < 0)
{
idx = size + idx;
}
if (idx < 0 || idx >= (ssize_t) size)
{
assert(size > 0);
err_push(&self->err, line,
"index out of bounds");
return;
}
char buf[2] = {target[idx], '\0'};
ccm_push(ccm, ccm_to_str(ccm, buf, line));
}
else if (ccm_is_tuple(ccm, ccm_target))
{
vec_t* target = ccm_from_tuple(ccm, ccm_target);
for (int i=0; i<param; i++) for (int i=0; i<param; i++)
{ {
CCM ccm_idx = ccm_pop(ccm); CCM ccm_idx = ccm_pop(ccm);
@ -69,17 +110,17 @@ void exec_instr(exec_t* self,
value_t* val = target->data[idx]; value_t* val = target->data[idx];
if (i == param - 1) if (i == param - 1) {
{
result = val; result = val;
} } else {
else
{
target = val->data.tuple; target = val->data.tuple;
} }
} }
ccm_push(ccm, ccm_from_value(ccm, result)); ccm_push(ccm, ccm_from_value(ccm, result));
} else {
assert(0);
}
self->pc++; self->pc++;
} break; } break;

View File

@ -144,6 +144,11 @@ node_t* lexer_try_new_next(lexer_t* self)
node_t* node = NULL; node_t* node = NULL;
if ( (node = lexer_try_new_str(self)) )
{
return node;
}
if ( (node = lexer_try_new_num(self)) ) if ( (node = lexer_try_new_num(self)) )
{ {
return node; return node;
@ -360,3 +365,73 @@ node_t* lexer_try_new_num(lexer_t* self)
return node; return node;
} }
node_t* lexer_try_new_str(lexer_t* self)
{
assert(self);
size_t cursor = self->cursor;
str_t value;
str_init(&value);
if (cursor >= strlen(self->source)
|| self->source[cursor] != '"')
{
str_free(&value);
return NULL;
}
cursor++;
while (cursor < strlen(self->source)
&& self->source[cursor] != '"')
{
if (self->source[cursor] == '\\'
&& cursor + 1 < strlen(self->source))
{
switch (self->source[cursor + 1])
{
case '\\': {
str_push(&value, '\\');
} break;
case 'n': {
str_push(&value, '\n');
} break;
case 'r': {
str_push(&value, '\r');
} break;
case 't': {
str_push(&value, '\t');
} break;
case 'e': {
str_push(&value, '\e');
} break;
case '"': {
str_push(&value, '"');
} break;
}
cursor += 2;
}
else {
str_push(&value, self->source[cursor]);
cursor++;
}
}
if (cursor >= strlen(self->source)
|| self->source[cursor] != '"')
{
str_free(&value);
return NULL;
}
cursor++;
self->cursor = cursor;
node_t* node = malloc(sizeof(node_t));
node_init(node, NODE_STR, value.value, self->line);
str_free(&value);
return node;
}

View File

@ -53,4 +53,5 @@ node_t* lexer_try_new_text(lexer_t* self,
int has_value); int has_value);
node_t* lexer_try_new_num(lexer_t* self); node_t* lexer_try_new_num(lexer_t* self);
node_t* lexer_try_new_str(lexer_t* self);
#endif #endif

View File

@ -46,11 +46,6 @@ int module_load(module_t* self, char const* path)
node_t* ast = parser_try_new_parse(&parser); node_t* ast = parser_try_new_parse(&parser);
if (!ast)
{
goto free_parser;
}
if (!err_is_ok(&lexer.err) || !err_is_ok(&parser.err)) if (!err_is_ok(&lexer.err) || !err_is_ok(&parser.err))
{ {
err_print_stack_trace(&lexer.err); err_print_stack_trace(&lexer.err);
@ -58,6 +53,12 @@ int module_load(module_t* self, char const* path)
err_push(&self->err, lexer.line, "invalid module"); err_push(&self->err, lexer.line, "invalid module");
goto free_parser; goto free_parser;
} }
if (!ast)
{
goto free_parser;
}
compiler_t compiler; compiler_t compiler;
compiler_init(&compiler, self); compiler_init(&compiler, self);

View File

@ -11,7 +11,8 @@ G(NODE_POW), G(NODE_ADD), G(NODE_SUB), G(NODE_MUL), \
G(NODE_DIV), G(NODE_MOD), G(NODE_COMMA), G(NODE_TUPLE), \ G(NODE_DIV), G(NODE_MOD), G(NODE_COMMA), G(NODE_TUPLE), \
G(NODE_ASSERT_EQ), G(NODE_ASSERT_NE), G(NODE_BOOL), \ G(NODE_ASSERT_EQ), G(NODE_ASSERT_NE), G(NODE_BOOL), \
G(NODE_AND), G(NODE_OR), G(NODE_NOT), G(NODE_IN), \ G(NODE_AND), G(NODE_OR), G(NODE_NOT), G(NODE_IN), \
G(NODE_OSQUARE), G(NODE_CSQUARE), G(NODE_INDEX) G(NODE_OSQUARE), G(NODE_CSQUARE), G(NODE_INDEX), \
G(NODE_STR)
CCM_ENUM_H(NodeKind, NODE_KIND); CCM_ENUM_H(NodeKind, NODE_KIND);

View File

@ -22,7 +22,14 @@ void parser_free(parser_t* self)
node_t* parser_try_new_parse(parser_t* self) node_t* parser_try_new_parse(parser_t* self)
{ {
assert(self); assert(self);
return CCM_TRY(parser_try_new_module); node_t* res = CCM_TRY(parser_try_new_module);
if (self->lexer->cursor < (ssize_t)strlen(self->lexer->source))
{
err_push(&self->err, res ? res->line : 0, "unexpected end");
}
return res;
} }
node_t* parser_try_new_rule_ll1(parser_t* self, node_t* parser_try_new_rule_ll1(parser_t* self,
@ -427,6 +434,7 @@ node_t* parser_try_new_literal(parser_t* self)
return tuple; return tuple;
} }
if (!lexer_consume_next(self->lexer, NODE_OPAR)) if (!lexer_consume_next(self->lexer, NODE_OPAR))
{ {
return NULL; return NULL;
@ -448,6 +456,13 @@ node_t* parser_try_new_literal(parser_t* self)
return expr; return expr;
} }
if (lexer_peek_kind(self->lexer, NODE_STR, 0)
&& lexer_peek_kind(self->lexer, NODE_OSQUARE, 1))
{
node_t* target = CCM_TRY(parser_try_new_builtin);
return CCM_TRY_LL1(parser_try_new_index, target);
}
return CCM_TRY(parser_try_new_builtin); return CCM_TRY(parser_try_new_builtin);
} }
@ -484,11 +499,6 @@ node_t* parser_try_new_index(parser_t* self, node_t* target)
return node; return node;
} }
node_t* parser_try_new_expr_lst(parser_t* self)
{
assert(self);
}
node_t* parser_try_new_tuple(parser_t* self) node_t* parser_try_new_tuple(parser_t* self)
{ {
assert(self); assert(self);
@ -554,6 +564,7 @@ node_t* parser_try_new_builtin(parser_t* self)
( (
node->kind == NODE_NUM node->kind == NODE_NUM
|| node->kind == NODE_BOOL || node->kind == NODE_BOOL
|| node->kind == NODE_STR
) )
) )
{ {

View File

@ -36,7 +36,6 @@ node_t* parser_try_new_not(parser_t* self);
node_t* parser_try_new_pow(parser_t* self); node_t* parser_try_new_pow(parser_t* self);
node_t* parser_try_new_literal(parser_t* self); node_t* parser_try_new_literal(parser_t* self);
node_t* parser_try_new_index(parser_t* self, node_t* target); node_t* parser_try_new_index(parser_t* self, node_t* target);
node_t* parser_try_new_expr_lst(parser_t* self);
node_t* parser_try_new_tuple(parser_t* self); node_t* parser_try_new_tuple(parser_t* self);
node_t* parser_try_new_builtin(parser_t* self); node_t* parser_try_new_builtin(parser_t* self);

View File

@ -6,7 +6,8 @@
#define TYPES(G) \ #define TYPES(G) \
G(TYPE_NUM), \ G(TYPE_NUM), \
G(TYPE_TUPLE), \ G(TYPE_TUPLE), \
G(TYPE_BOOLEAN) G(TYPE_BOOLEAN), \
G(TYPE_STR)
CCM_ENUM_H(Type, TYPES); CCM_ENUM_H(Type, TYPES);

View File

@ -25,6 +25,16 @@ void value_init_boolean(value_t* self, int boolean, int line)
self->line = line; self->line = line;
} }
void value_init_str(value_t* self, char const* value, int line)
{
assert(self);
assert(value);
self->data.str = strdup(value);
self->type = TYPE_STR;
self->line = line;
}
value_t* value_new_clone(value_t* self) value_t* value_new_clone(value_t* self)
{ {
assert(self); assert(self);
@ -33,6 +43,9 @@ value_t* value_new_clone(value_t* self)
switch (self->type) switch (self->type)
{ {
case TYPE_STR: {
value_init_str(value, self->data.str, self->line);
} break;
case TYPE_NUM: { case TYPE_NUM: {
value_init_num(value, self->data.num, self->line); value_init_num(value, self->data.num, self->line);
} break; } break;
@ -72,6 +85,11 @@ void value_free(value_t* self)
vec_free(self->data.tuple); vec_free(self->data.tuple);
free(self->data.tuple); free(self->data.tuple);
} }
if (self->type == TYPE_STR)
{
free(self->data.str);
}
} }
size_t value_str(value_t* self, char* buffer, size_t size) size_t value_str(value_t* self, char* buffer, size_t size)
@ -82,6 +100,11 @@ size_t value_str(value_t* self, char* buffer, size_t size)
switch (self->type) switch (self->type)
{ {
case TYPE_STR: {
sz += snprintf(buffer + sz, size - sz, "%s",
self->data.str);
} break;
case TYPE_NUM: { case TYPE_NUM: {
sz += snprintf(buffer + sz, size - sz, "%lf", sz += snprintf(buffer + sz, size - sz, "%lf",
self->data.num); self->data.num);
@ -129,6 +152,10 @@ int value_equals(value_t* self, value_t* rhs)
switch (self->type) switch (self->type)
{ {
case TYPE_STR: {
return strcmp(self->data.str, rhs->data.str) == 0;
} break;
case TYPE_NUM: { case TYPE_NUM: {
return self->data.num == rhs->data.num; return self->data.num == rhs->data.num;
} break; } break;

View File

@ -10,6 +10,7 @@ typedef struct {
double num; double num;
vec_t* tuple; vec_t* tuple;
int boolean; int boolean;
char* str;
} data; } data;
Type type; Type type;
@ -19,6 +20,7 @@ typedef struct {
void value_init_num(value_t* self, double num, int line); void value_init_num(value_t* self, double num, int line);
void value_init_new_tuple(value_t* self, vec_t* values, int line); void value_init_new_tuple(value_t* self, vec_t* values, int line);
void value_init_boolean(value_t* self, int boolean, int line); void value_init_boolean(value_t* self, int boolean, int line);
void value_init_str(value_t* self, char const* value, int line);
value_t* value_new_clone(value_t* self); value_t* value_new_clone(value_t* self);
void value_free(value_t* self); void value_free(value_t* self);