✨ strings arithmetic.
parent
5cb141e4cf
commit
e7e0789cbb
|
@ -17,3 +17,4 @@ BUILTIN ::=
|
||||||
| int
|
| int
|
||||||
| bool
|
| bool
|
||||||
| float
|
| float
|
||||||
|
| string
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
assert "hello" eq "hello"
|
||||||
|
|
||||||
|
assert "hello " + "world" eq "hello world"
|
||||||
|
assert "a" + "b" + "c" eq "abc"
|
||||||
|
|
||||||
|
assert "a" * 3 eq "aaa"
|
||||||
|
assert 4 * "b" eq "bbbb"
|
||||||
|
assert ("a" + "b") * 2 eq "abab"
|
|
@ -31,6 +31,7 @@ void lexer_consume_next(struct lexer* self);
|
||||||
struct token* lexer_try_new_next(struct lexer* self);
|
struct token* lexer_try_new_next(struct lexer* self);
|
||||||
struct token* lexer_try_scan_int(struct lexer* self);
|
struct token* lexer_try_scan_int(struct lexer* self);
|
||||||
struct token* lexer_try_scan_float(struct lexer* self);
|
struct token* lexer_try_scan_float(struct lexer* self);
|
||||||
|
struct token* lexer_try_scan_string(struct lexer* self);
|
||||||
struct token* lexer_try_scan_text(struct lexer* self,
|
struct token* lexer_try_scan_text(struct lexer* self,
|
||||||
char const* text,
|
char const* text,
|
||||||
TokenKind kind);
|
TokenKind kind);
|
||||||
|
|
|
@ -10,7 +10,7 @@ G(NODE_ADD), G(NODE_SUB), G(NODE_MUL),\
|
||||||
G(NODE_DIV), G(NODE_POW), G(NODE_MOD),\
|
G(NODE_DIV), G(NODE_POW), G(NODE_MOD),\
|
||||||
G(NODE_USUB), G(NODE_ASSERT_EQ), \
|
G(NODE_USUB), G(NODE_ASSERT_EQ), \
|
||||||
G(NODE_BOOL), G(NODE_AND), G(NODE_OR), \
|
G(NODE_BOOL), G(NODE_AND), G(NODE_OR), \
|
||||||
G(NODE_NOT), G(NODE_FLOAT)
|
G(NODE_NOT), G(NODE_FLOAT), G(NODE_STRING)
|
||||||
|
|
||||||
SK_ENUM_H(NodeKind, NODE_KIND);
|
SK_ENUM_H(NodeKind, NODE_KIND);
|
||||||
|
|
||||||
|
|
|
@ -52,8 +52,10 @@ SK state_push(struct state* self,
|
||||||
SK state_push_int(struct state* self, int integer, int line);
|
SK state_push_int(struct state* self, int integer, int line);
|
||||||
SK state_push_bool(struct state* self, bool boolean, int line);
|
SK state_push_bool(struct state* self, bool boolean, int line);
|
||||||
SK state_push_float(struct state* self, double real, int line);
|
SK state_push_float(struct state* self, double real, int line);
|
||||||
|
SK state_push_string(struct state* self, char const* str, int line);
|
||||||
|
|
||||||
TypeKind state_common_num_type(struct state* self, SK lhs, SK rhs);
|
TypeKind state_common_num_type(struct state* self, SK lhs, SK rhs);
|
||||||
|
TypeKind state_type(struct state* self, SK value);
|
||||||
double state_as_real(struct state* self, SK lhs);
|
double state_as_real(struct state* self, SK lhs);
|
||||||
int state_line(struct state* self, SK lhs);
|
int state_line(struct state* self, SK lhs);
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ G(TOKEN_MUL), G(TOKEN_DIV), G(TOKEN_MOD), \
|
||||||
G(TOKEN_POW), G(TOKEN_OPAR), G(TOKEN_CPAR), \
|
G(TOKEN_POW), G(TOKEN_OPAR), G(TOKEN_CPAR), \
|
||||||
G(TOKEN_ASSERT), G(TOKEN_ASSERT_EQ), \
|
G(TOKEN_ASSERT), G(TOKEN_ASSERT_EQ), \
|
||||||
G(TOKEN_BOOL), G(TOKEN_AND), G(TOKEN_OR), \
|
G(TOKEN_BOOL), G(TOKEN_AND), G(TOKEN_OR), \
|
||||||
G(TOKEN_NOT), G(TOKEN_FLOAT)
|
G(TOKEN_NOT), G(TOKEN_FLOAT), G(TOKEN_STRING)
|
||||||
|
|
||||||
SK_ENUM_H(TokenKind, TOKEN_KIND);
|
SK_ENUM_H(TokenKind, TOKEN_KIND);
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,8 @@
|
||||||
#include "node.h"
|
#include "node.h"
|
||||||
|
|
||||||
#define TYPE_KIND(G) \
|
#define TYPE_KIND(G) \
|
||||||
G(TYPE_INT), G(TYPE_BOOL), G(TYPE_FLOAT)
|
G(TYPE_INT), G(TYPE_BOOL), G(TYPE_FLOAT), \
|
||||||
|
G(TYPE_STRING)
|
||||||
|
|
||||||
SK_ENUM_H(TypeKind, TYPE_KIND);
|
SK_ENUM_H(TypeKind, TYPE_KIND);
|
||||||
|
|
||||||
|
@ -14,6 +15,7 @@ union val
|
||||||
int integer;
|
int integer;
|
||||||
double real;
|
double real;
|
||||||
bool boolean;
|
bool boolean;
|
||||||
|
char* str;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct value
|
struct value
|
||||||
|
|
|
@ -69,6 +69,13 @@ void compiler_compile(struct compiler* self,
|
||||||
val);
|
val);
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
case NODE_STRING: {
|
||||||
|
union val val;
|
||||||
|
val.str = strdup(node->token->value);
|
||||||
|
compiler_compile_value(self, node, prog, TYPE_STRING,
|
||||||
|
val);
|
||||||
|
} break;
|
||||||
|
|
||||||
case NODE_ADD: {
|
case NODE_ADD: {
|
||||||
compiler_compile_children(self, node, prog);
|
compiler_compile_children(self, node, prog);
|
||||||
prog_add_instr(prog, OP_ADD, SK_NO_PARAM);
|
prog_add_instr(prog, OP_ADD, SK_NO_PARAM);
|
||||||
|
|
|
@ -82,6 +82,14 @@ void exec_execute(struct exec* self,
|
||||||
);
|
);
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
case TYPE_STRING: {
|
||||||
|
state_push_string(
|
||||||
|
state,
|
||||||
|
constant->val.str,
|
||||||
|
constant->line
|
||||||
|
);
|
||||||
|
} break;
|
||||||
|
|
||||||
case TYPE_BOOL: {
|
case TYPE_BOOL: {
|
||||||
state_push_bool(
|
state_push_bool(
|
||||||
state,
|
state,
|
||||||
|
|
|
@ -143,6 +143,10 @@ struct token* lexer_try_new_next(struct lexer* self)
|
||||||
return tok;
|
return tok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( (tok=lexer_try_scan_string(self)) )
|
||||||
|
{
|
||||||
|
return tok;
|
||||||
|
}
|
||||||
SK_SCAN_TEXT("+", TOKEN_ADD);
|
SK_SCAN_TEXT("+", TOKEN_ADD);
|
||||||
SK_SCAN_TEXT("-", TOKEN_SUB);
|
SK_SCAN_TEXT("-", TOKEN_SUB);
|
||||||
SK_SCAN_TEXT("*", TOKEN_MUL);
|
SK_SCAN_TEXT("*", TOKEN_MUL);
|
||||||
|
@ -271,6 +275,88 @@ struct token* lexer_try_scan_float(struct lexer* self)
|
||||||
return tok;
|
return tok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct token* lexer_try_scan_string(struct lexer* self)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
size_t cursor = self->context.cursor;
|
||||||
|
|
||||||
|
if (cursor >= self->len
|
||||||
|
|| self->source[cursor] != '"')
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
cursor++;
|
||||||
|
|
||||||
|
struct str value;
|
||||||
|
str_init(&value);
|
||||||
|
bool escaped = false;
|
||||||
|
|
||||||
|
while (cursor < self->len
|
||||||
|
&& self->source[cursor] != '"')
|
||||||
|
{
|
||||||
|
if (self->source[cursor] == '\\')
|
||||||
|
{
|
||||||
|
escaped = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (escaped)
|
||||||
|
{
|
||||||
|
cursor++;
|
||||||
|
|
||||||
|
switch (self->source[cursor])
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
default: {
|
||||||
|
errors_push(self->context.line,
|
||||||
|
"unknown escape symbol");
|
||||||
|
str_free(&value);
|
||||||
|
return NULL;
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
|
||||||
|
escaped = false;
|
||||||
|
cursor++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
str_push(&value, self->source[cursor]);
|
||||||
|
cursor++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cursor >= self->len
|
||||||
|
|| self->source[cursor] != '"')
|
||||||
|
{
|
||||||
|
str_free(&value);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
cursor++;
|
||||||
|
|
||||||
|
struct token* tok = malloc(sizeof(struct token));
|
||||||
|
token_init(tok, TOKEN_STRING, value.value, self->context.line);
|
||||||
|
self->context.cursor = cursor;
|
||||||
|
str_free(&value);
|
||||||
|
return tok;
|
||||||
|
}
|
||||||
|
|
||||||
struct token* lexer_try_scan_text(struct lexer* self,
|
struct token* lexer_try_scan_text(struct lexer* self,
|
||||||
char const* text,
|
char const* text,
|
||||||
TokenKind kind)
|
TokenKind kind)
|
||||||
|
|
|
@ -440,5 +440,16 @@ struct node* parser_try_builtin(struct parser* self)
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (lexer_next_is(&self->lexer, TOKEN_STRING))
|
||||||
|
{
|
||||||
|
struct node* node = malloc(sizeof(struct node));
|
||||||
|
|
||||||
|
node_init(node,
|
||||||
|
NODE_STRING,
|
||||||
|
lexer_try_new_next(&self->lexer));
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
|
@ -154,6 +154,14 @@ SK state_push_float(struct state* self, double real, int line)
|
||||||
return state_push(self, TYPE_FLOAT, val ,line);
|
return state_push(self, TYPE_FLOAT, val ,line);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SK state_push_string(struct state* self, char const* str, int line)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
union val val;
|
||||||
|
val.str = strdup(str);
|
||||||
|
return state_push(self, TYPE_STRING, val ,line);
|
||||||
|
}
|
||||||
|
|
||||||
TypeKind state_common_num_type(struct state* self, SK lhs, SK rhs)
|
TypeKind state_common_num_type(struct state* self, SK lhs, SK rhs)
|
||||||
{
|
{
|
||||||
assert(self);
|
assert(self);
|
||||||
|
@ -170,6 +178,13 @@ TypeKind state_common_num_type(struct state* self, SK lhs, SK rhs)
|
||||||
return TYPE_INT;
|
return TYPE_INT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TypeKind state_type(struct state* self, SK value)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
struct value const* val = state_try_get_value(self, value);
|
||||||
|
return val->type;
|
||||||
|
}
|
||||||
|
|
||||||
double state_as_real(struct state* self, SK lhs)
|
double state_as_real(struct state* self, SK lhs)
|
||||||
{
|
{
|
||||||
assert(self);
|
assert(self);
|
||||||
|
@ -204,7 +219,20 @@ SK state_add(struct state* self)
|
||||||
|
|
||||||
TypeKind type = state_common_num_type(self, lhs, rhs);
|
TypeKind type = state_common_num_type(self, lhs, rhs);
|
||||||
|
|
||||||
if (type == TYPE_INT)
|
if(state_type(self, lhs) == TYPE_STRING
|
||||||
|
&& state_type(self, rhs) == TYPE_STRING)
|
||||||
|
{
|
||||||
|
struct value* left = state_try_get_value(self, lhs);
|
||||||
|
struct value* right = state_try_get_value(self, rhs);
|
||||||
|
struct str val;
|
||||||
|
str_init(&val);
|
||||||
|
str_extend(&val, left->val.str);
|
||||||
|
str_extend(&val, right->val.str);
|
||||||
|
SK res = state_push_string(self, val.value, left->line);
|
||||||
|
str_free(&val);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
else if (type == TYPE_INT)
|
||||||
{
|
{
|
||||||
return state_push_int(
|
return state_push_int(
|
||||||
self,
|
self,
|
||||||
|
@ -296,7 +324,37 @@ SK state_mul(struct state* self)
|
||||||
|
|
||||||
TypeKind type = state_common_num_type(self, lhs, rhs);
|
TypeKind type = state_common_num_type(self, lhs, rhs);
|
||||||
|
|
||||||
if (type == TYPE_INT)
|
if(state_type(self, lhs) == TYPE_STRING
|
||||||
|
&& state_type(self, rhs) == TYPE_INT)
|
||||||
|
{
|
||||||
|
struct value* left = state_try_get_value(self, lhs);
|
||||||
|
struct value* right = state_try_get_value(self, rhs);
|
||||||
|
struct str val;
|
||||||
|
str_init(&val);
|
||||||
|
for (int i=0; i<right->val.integer; i++)
|
||||||
|
{
|
||||||
|
str_extend(&val, left->val.str);
|
||||||
|
}
|
||||||
|
SK res = state_push_string(self, val.value, left->line);
|
||||||
|
str_free(&val);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
else if(state_type(self, lhs) == TYPE_INT
|
||||||
|
&& state_type(self, rhs) == TYPE_STRING)
|
||||||
|
{
|
||||||
|
struct value* left = state_try_get_value(self, lhs);
|
||||||
|
struct value* right = state_try_get_value(self, rhs);
|
||||||
|
struct str val;
|
||||||
|
str_init(&val);
|
||||||
|
for (int i=0; i<left->val.integer; i++)
|
||||||
|
{
|
||||||
|
str_extend(&val, right->val.str);
|
||||||
|
}
|
||||||
|
SK res = state_push_string(self, val.value, left->line);
|
||||||
|
str_free(&val);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
else if (type == TYPE_INT)
|
||||||
{
|
{
|
||||||
return state_push_int(
|
return state_push_int(
|
||||||
self,
|
self,
|
||||||
|
|
|
@ -16,6 +16,10 @@ void value_init(struct value* self,
|
||||||
void value_free(struct value* self)
|
void value_free(struct value* self)
|
||||||
{
|
{
|
||||||
assert(self);
|
assert(self);
|
||||||
|
if (self->type == TYPE_STRING)
|
||||||
|
{
|
||||||
|
free(self->val.str);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool value_equals(struct value* self, struct value* rhs)
|
bool value_equals(struct value* self, struct value* rhs)
|
||||||
|
@ -43,6 +47,10 @@ void value_str(struct value* self, struct str* dest)
|
||||||
|
|
||||||
switch (self->type)
|
switch (self->type)
|
||||||
{
|
{
|
||||||
|
case TYPE_STRING: {
|
||||||
|
str_format(dest, "%s", self->val.str);
|
||||||
|
} break;
|
||||||
|
|
||||||
case TYPE_INT: {
|
case TYPE_INT: {
|
||||||
str_format(dest, "%d", self->val.integer);
|
str_format(dest, "%d", self->val.integer);
|
||||||
} break;
|
} break;
|
||||||
|
|
|
@ -16,6 +16,10 @@ static void test_lexer(char const* source, int count, ...)
|
||||||
for (int i=0; i<count; i++)
|
for (int i=0; i<count; i++)
|
||||||
{
|
{
|
||||||
struct token* tok = lexer_try_new_next(&lexer);
|
struct token* tok = lexer_try_new_next(&lexer);
|
||||||
|
if (tok == NULL)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s == NULL\n", source);
|
||||||
|
}
|
||||||
CU_ASSERT_FATAL(tok != NULL);
|
CU_ASSERT_FATAL(tok != NULL);
|
||||||
struct str tok_str;
|
struct str tok_str;
|
||||||
str_init(&tok_str);
|
str_init(&tok_str);
|
||||||
|
@ -81,6 +85,17 @@ static void test_lexer_float()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void test_lexer_string()
|
||||||
|
{
|
||||||
|
test_lexer("\" hello \" \"\\\"world\\\"\"", 2,
|
||||||
|
"STRING[ hello ]",
|
||||||
|
"STRING[\"world\"]"
|
||||||
|
);
|
||||||
|
|
||||||
|
test_lexer("\"\\n\\r\\t\\e\"", 1,
|
||||||
|
"STRING[\n\r\t\e]"
|
||||||
|
);
|
||||||
|
}
|
||||||
void register_lexer()
|
void register_lexer()
|
||||||
{
|
{
|
||||||
CU_pSuite suite = CU_add_suite("Lexer", 0, 0);
|
CU_pSuite suite = CU_add_suite("Lexer", 0, 0);
|
||||||
|
@ -88,6 +103,7 @@ void register_lexer()
|
||||||
CU_add_test(suite, "Assertions", test_lexer_assert);
|
CU_add_test(suite, "Assertions", test_lexer_assert);
|
||||||
CU_add_test(suite, "Booleans", test_lexer_bool);
|
CU_add_test(suite, "Booleans", test_lexer_bool);
|
||||||
CU_add_test(suite, "Floats", test_lexer_float);
|
CU_add_test(suite, "Floats", test_lexer_float);
|
||||||
|
CU_add_test(suite, "Strings", test_lexer_string);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -64,12 +64,20 @@ static void test_parser_bool()
|
||||||
"BOOL[false])))",
|
"BOOL[false])))",
|
||||||
"not false and (true or false)");
|
"not false and (true or false)");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void test_parser_string()
|
||||||
|
{
|
||||||
|
test_parser("ROOT(STRING[ok pizza NOW])",
|
||||||
|
"\"ok pizza NOW\"");
|
||||||
|
}
|
||||||
|
|
||||||
void register_parser()
|
void register_parser()
|
||||||
{
|
{
|
||||||
CU_pSuite suite = CU_add_suite("Parser", 0, 0);
|
CU_pSuite suite = CU_add_suite("Parser", 0, 0);
|
||||||
CU_add_test(suite, "Integers", test_parser_int);
|
CU_add_test(suite, "Integers", test_parser_int);
|
||||||
CU_add_test(suite, "Assertions", test_parser_assert);
|
CU_add_test(suite, "Assertions", test_parser_assert);
|
||||||
CU_add_test(suite, "Booleans", test_parser_bool);
|
CU_add_test(suite, "Booleans", test_parser_bool);
|
||||||
|
CU_add_test(suite, "Strings", test_parser_string);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in New Issue