✨ int, float and strings.
parent
5a67acd796
commit
dd5c65b448
|
@ -34,6 +34,27 @@ int compiler_compile(struct compiler* self,
|
||||||
program_push_instr(program, OP_PUSH, addr);
|
program_push_instr(program, OP_PUSH, addr);
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
case NODE_INT: {
|
||||||
|
struct value* val = malloc(sizeof(struct value));
|
||||||
|
value_init_int(val, atoi(node->value), node->line);
|
||||||
|
size_t addr = program_push_constant(program, val);
|
||||||
|
program_push_instr(program, OP_PUSH, addr);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case NODE_FLOAT: {
|
||||||
|
struct value* val = malloc(sizeof(struct value));
|
||||||
|
value_init_float(val, atof(node->value), node->line);
|
||||||
|
size_t addr = program_push_constant(program, val);
|
||||||
|
program_push_instr(program, OP_PUSH, addr);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case NODE_STRING: {
|
||||||
|
struct value* val = malloc(sizeof(struct value));
|
||||||
|
value_init_string(val, node->value, node->line);
|
||||||
|
size_t addr = program_push_constant(program, val);
|
||||||
|
program_push_instr(program, OP_PUSH, addr);
|
||||||
|
} break;
|
||||||
|
|
||||||
case NODE_ASSERT: {
|
case NODE_ASSERT: {
|
||||||
compiler_compile(self, node->children.data[0], program);
|
compiler_compile(self, node->children.data[0], program);
|
||||||
size_t brt = program_push_instr(program, OP_BRT, NO_PARAM);
|
size_t brt = program_push_instr(program, OP_BRT, NO_PARAM);
|
||||||
|
@ -55,6 +76,19 @@ int compiler_compile(struct compiler* self,
|
||||||
|
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
case NODE_EQ: {
|
||||||
|
compiler_compile(self, node->children.data[0], program);
|
||||||
|
compiler_compile(self, node->children.data[1], program);
|
||||||
|
program_push_instr(program, OP_EQ, NO_PARAM);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case NODE_NE: {
|
||||||
|
compiler_compile(self, node->children.data[0], program);
|
||||||
|
compiler_compile(self, node->children.data[1], program);
|
||||||
|
program_push_instr(program, OP_EQ, NO_PARAM);
|
||||||
|
program_push_instr(program, OP_NOT, NO_PARAM);
|
||||||
|
} break;
|
||||||
|
|
||||||
case NODE_NOT: {
|
case NODE_NOT: {
|
||||||
compiler_compile(self, node->children.data[0], program);
|
compiler_compile(self, node->children.data[0], program);
|
||||||
program_push_instr(program, OP_NOT, NO_PARAM);
|
program_push_instr(program, OP_NOT, NO_PARAM);
|
||||||
|
@ -158,6 +192,40 @@ int compiler_compile(struct compiler* self,
|
||||||
vec_free(&to_pivot);
|
vec_free(&to_pivot);
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
case NODE_ADD:
|
||||||
|
case NODE_SUB:
|
||||||
|
case NODE_MUL:
|
||||||
|
case NODE_DIV:
|
||||||
|
case NODE_MOD:
|
||||||
|
case NODE_POW:
|
||||||
|
case NODE_LT:
|
||||||
|
case NODE_LE:
|
||||||
|
case NODE_GT:
|
||||||
|
case NODE_GE:
|
||||||
|
{
|
||||||
|
|
||||||
|
for (size_t i=0; i<node->children.size; i++)
|
||||||
|
{
|
||||||
|
compiler_compile(self, node->children.data[i], program);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (node->type)
|
||||||
|
{
|
||||||
|
case NODE_ADD: program_push_instr(program, OP_ADD, NO_PARAM); break;
|
||||||
|
case NODE_SUB: program_push_instr(program, OP_SUB, NO_PARAM); break;
|
||||||
|
case NODE_MUL: program_push_instr(program, OP_MUL, NO_PARAM); break;
|
||||||
|
case NODE_DIV: program_push_instr(program, OP_DIV, NO_PARAM); break;
|
||||||
|
case NODE_MOD: program_push_instr(program, OP_MOD, NO_PARAM); break;
|
||||||
|
case NODE_POW: program_push_instr(program, OP_POW, NO_PARAM); break;
|
||||||
|
case NODE_LT: program_push_instr(program, OP_LT, NO_PARAM); break;
|
||||||
|
case NODE_LE: program_push_instr(program, OP_LE, NO_PARAM); break;
|
||||||
|
case NODE_GT: program_push_instr(program, OP_GT, NO_PARAM); break;
|
||||||
|
case NODE_GE: program_push_instr(program, OP_GE, NO_PARAM); break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
|
||||||
|
} break;
|
||||||
|
|
||||||
default: {
|
default: {
|
||||||
for (size_t i=0; i<node->children.size; i++)
|
for (size_t i=0; i<node->children.size; i++)
|
||||||
{
|
{
|
||||||
|
|
|
@ -3,7 +3,9 @@
|
||||||
|
|
||||||
#define OPCODES(G) \
|
#define OPCODES(G) \
|
||||||
G(OP_PUSH), G(OP_POP), G(OP_BRF), G(OP_BR), G(OP_HALT), \
|
G(OP_PUSH), G(OP_POP), G(OP_BRF), G(OP_BR), G(OP_HALT), \
|
||||||
G(OP_BRT), G(OP_NOP), G(OP_NOT)
|
G(OP_BRT), G(OP_NOP), G(OP_NOT), G(OP_EQ), \
|
||||||
|
G(OP_ADD), G(OP_SUB), G(OP_MUL), G(OP_DIV), G(OP_MOD), G(OP_POW), \
|
||||||
|
G(OP_LT), G(OP_LE), G(OP_GT),G(OP_GE),
|
||||||
|
|
||||||
#include <commons.h>
|
#include <commons.h>
|
||||||
|
|
||||||
|
|
11
doc/gux.bnf
11
doc/gux.bnf
|
@ -8,8 +8,13 @@ EXPR ::=
|
||||||
| ASSERT EXPR
|
| ASSERT EXPR
|
||||||
|
|
||||||
OR ::= AND (or AND)*
|
OR ::= AND (or AND)*
|
||||||
AND ::= NOT (and NOT)*
|
AND ::= EQNE (and EQNE)*
|
||||||
NOT ::= not* LITERAL
|
EQNE ::= CMP ((eq|ne) CMP)?
|
||||||
|
CMP ::= TERM ((lt|le|gt|ge) TERM)?
|
||||||
|
TERM ::= FACTOR ((add|sub) FACTOR)*
|
||||||
|
FACTOR ::= POW ((mul|div|mod) POW)*
|
||||||
|
POW ::= NOT (pow NOT)?
|
||||||
|
|
||||||
|
NOT ::= not* LITERAL
|
||||||
LITERAL ::= BUILTIN | opar EXPR cpar
|
LITERAL ::= BUILTIN | opar EXPR cpar
|
||||||
BUILTIN ::= bool
|
BUILTIN ::= bool | int | float | string
|
||||||
|
|
|
@ -275,7 +275,7 @@ int main(int argc, char** argv)
|
||||||
if (show_stack)
|
if (show_stack)
|
||||||
{
|
{
|
||||||
char buffer[GUX_STR_SIZE];
|
char buffer[GUX_STR_SIZE];
|
||||||
vm_str(&vm, buffer, GUX_STR_SIZE);
|
vm_str(&vm, &program, buffer, GUX_STR_SIZE);
|
||||||
printf("%s\n", buffer);
|
printf("%s\n", buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,9 +24,10 @@ target_include_directories(gux-lang
|
||||||
)
|
)
|
||||||
|
|
||||||
target_link_libraries(gux-lang
|
target_link_libraries(gux-lang
|
||||||
PUBLIC gux-lib
|
PUBLIC gux-lib m
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
# =====
|
# =====
|
||||||
# TESTS
|
# TESTS
|
||||||
# =====
|
# =====
|
||||||
|
|
199
lang/src/lexer.c
199
lang/src/lexer.c
|
@ -1,4 +1,5 @@
|
||||||
#include "lexer.h"
|
#include "lexer.h"
|
||||||
|
#include "commons.h"
|
||||||
|
|
||||||
void lexer_init(struct lexer* self, char const* source)
|
void lexer_init(struct lexer* self, char const* source)
|
||||||
{
|
{
|
||||||
|
@ -11,7 +12,22 @@ void lexer_init(struct lexer* self, char const* source)
|
||||||
|
|
||||||
vec_init(&self->toks, 1);
|
vec_init(&self->toks, 1);
|
||||||
|
|
||||||
|
lexer_add_tok(self, "<", NODE_LT, 0);
|
||||||
|
lexer_add_tok(self, "<=", NODE_LE, 0);
|
||||||
|
lexer_add_tok(self, ">", NODE_GT, 0);
|
||||||
|
lexer_add_tok(self, ">=", NODE_GE, 0);
|
||||||
|
|
||||||
|
lexer_add_tok(self, "+", NODE_ADD, 0);
|
||||||
|
lexer_add_tok(self, "-", NODE_SUB, 0);
|
||||||
|
lexer_add_tok(self, "*", NODE_MUL, 0);
|
||||||
|
lexer_add_tok(self, "/", NODE_DIV, 0);
|
||||||
|
lexer_add_tok(self, "%", NODE_MOD, 0);
|
||||||
|
lexer_add_tok(self, "**", NODE_POW, 0);
|
||||||
|
|
||||||
|
lexer_add_tok(self, "==", NODE_EQ, 0);
|
||||||
|
lexer_add_tok(self, "!=", NODE_NE, 0);
|
||||||
lexer_add_tok(self, "&&", NODE_AND, 0);
|
lexer_add_tok(self, "&&", NODE_AND, 0);
|
||||||
|
|
||||||
lexer_add_tok(self, "||", NODE_OR, 0);
|
lexer_add_tok(self, "||", NODE_OR, 0);
|
||||||
lexer_add_tok(self, "!", NODE_NOT, 0);
|
lexer_add_tok(self, "!", NODE_NOT, 0);
|
||||||
lexer_add_tok(self, "(", NODE_OPAR, 0);
|
lexer_add_tok(self, "(", NODE_OPAR, 0);
|
||||||
|
@ -71,16 +87,33 @@ struct node* lexer_next_new(struct lexer* self)
|
||||||
|
|
||||||
struct token_info info;
|
struct token_info info;
|
||||||
|
|
||||||
|
if (lexer_scan_float(self, &info))
|
||||||
|
{
|
||||||
|
struct node* node = malloc(sizeof(struct node));
|
||||||
|
node_init(node, info.type, info.value, self->line);
|
||||||
|
self->cursor = info.position;
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lexer_scan_int(self, &info))
|
||||||
|
{
|
||||||
|
struct node* node = malloc(sizeof(struct node));
|
||||||
|
node_init(node, info.type, info.value, self->line);
|
||||||
|
self->cursor = info.position;
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
struct node* best = NULL;
|
struct node* best = NULL;
|
||||||
size_t pos = 0;
|
size_t pos = 0;
|
||||||
|
|
||||||
|
// tokens
|
||||||
for (size_t i=0; i<self->toks.size; i++)
|
for (size_t i=0; i<self->toks.size; i++)
|
||||||
{
|
{
|
||||||
struct tok* tok = self->toks.data[i];
|
struct tok* tok = self->toks.data[i];
|
||||||
|
|
||||||
if (lexer_scan_text(self, tok->sym, &info))
|
if (lexer_scan_text(self, tok->sym, &info))
|
||||||
{
|
{
|
||||||
if (best == NULL || pos > info.position)
|
if (best == NULL || info.position > pos)
|
||||||
{
|
{
|
||||||
struct node* node = malloc(sizeof(struct node));
|
struct node* node = malloc(sizeof(struct node));
|
||||||
node_init(node, tok->type, "", self->line);
|
node_init(node, tok->type, "", self->line);
|
||||||
|
@ -88,13 +121,12 @@ struct node* lexer_next_new(struct lexer* self)
|
||||||
if (best)
|
if (best)
|
||||||
{
|
{
|
||||||
node_free(best);
|
node_free(best);
|
||||||
|
free(best);
|
||||||
}
|
}
|
||||||
|
|
||||||
best = node;
|
best = node;
|
||||||
pos = info.position;
|
pos = info.position;
|
||||||
}
|
}
|
||||||
|
|
||||||
self->cursor = info.position;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,6 +153,14 @@ struct node* lexer_next_new(struct lexer* self)
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (lexer_scan_string(self, &info))
|
||||||
|
{
|
||||||
|
struct node* node = malloc(sizeof(struct node));
|
||||||
|
node_init(node, info.type, info.value, self->line);
|
||||||
|
self->cursor = info.position;
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -191,7 +231,7 @@ int lexer_scan_keyword(struct lexer* self,
|
||||||
}
|
}
|
||||||
|
|
||||||
info->position = self->cursor + strlen(keyword);
|
info->position = self->cursor + strlen(keyword);
|
||||||
info->value = keyword;
|
memcpy(info->value, keyword, GUX_STR_SIZE * sizeof(char));
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -218,11 +258,160 @@ int lexer_scan_text(struct lexer* self,
|
||||||
}
|
}
|
||||||
|
|
||||||
info->position = self->cursor + strlen(text);
|
info->position = self->cursor + strlen(text);
|
||||||
info->value = text;
|
memcpy(info->value, text, GUX_STR_SIZE * sizeof(char));
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int lexer_scan_int(struct lexer* self,
|
||||||
|
struct token_info* info)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
assert(info);
|
||||||
|
|
||||||
|
size_t cursor = self->cursor;
|
||||||
|
char value[GUX_STR_SIZE];
|
||||||
|
memset(value, 0, GUX_STR_SIZE);
|
||||||
|
size_t sz = 0;
|
||||||
|
|
||||||
|
if (cursor < strlen(self->source)
|
||||||
|
&& self->source[cursor] == '-')
|
||||||
|
{
|
||||||
|
value[sz++] = '-';
|
||||||
|
cursor++;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (cursor < strlen(self->source)
|
||||||
|
&& isdigit(self->source[cursor]))
|
||||||
|
{
|
||||||
|
value[sz] = self->source[cursor];
|
||||||
|
cursor++;
|
||||||
|
sz++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sz == 1 && value[0] == '-')
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sz > 0)
|
||||||
|
{
|
||||||
|
info->position = cursor;
|
||||||
|
info->type = NODE_INT;
|
||||||
|
memcpy(info->value, value, GUX_STR_SIZE * sizeof(char));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int lexer_scan_float(struct lexer* self,
|
||||||
|
struct token_info* info)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
assert(info);
|
||||||
|
size_t cursor = self->cursor;
|
||||||
|
char value[GUX_STR_SIZE];
|
||||||
|
memset(value, 0, GUX_STR_SIZE);
|
||||||
|
size_t sz = 0;
|
||||||
|
|
||||||
|
if (cursor < strlen(self->source)
|
||||||
|
&& self->source[cursor] == '-')
|
||||||
|
{
|
||||||
|
value[sz++] = '-';
|
||||||
|
cursor++;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (cursor < strlen(self->source)
|
||||||
|
&& isdigit(self->source[cursor]))
|
||||||
|
{
|
||||||
|
value[sz] = self->source[cursor];
|
||||||
|
cursor++;
|
||||||
|
sz++;
|
||||||
|
}
|
||||||
|
|
||||||
|
int is_float = 0;
|
||||||
|
|
||||||
|
if (cursor < strlen(self->source)
|
||||||
|
&& self->source[cursor] == '.')
|
||||||
|
{
|
||||||
|
value[sz++] = '.';
|
||||||
|
is_float = 1;
|
||||||
|
cursor++;
|
||||||
|
|
||||||
|
while (cursor < strlen(self->source)
|
||||||
|
&& isdigit(self->source[cursor]))
|
||||||
|
{
|
||||||
|
value[sz] = self->source[cursor];
|
||||||
|
cursor++;
|
||||||
|
sz++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sz > 0 && is_float)
|
||||||
|
{
|
||||||
|
info->position = cursor;
|
||||||
|
info->type = NODE_FLOAT;
|
||||||
|
memcpy(info->value, value, GUX_STR_SIZE * sizeof(char));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int lexer_scan_string(struct lexer* self,
|
||||||
|
struct token_info* info)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
assert(info);
|
||||||
|
|
||||||
|
size_t cursor = self->cursor;
|
||||||
|
char value[GUX_STR_SIZE];
|
||||||
|
memset(value, 0, GUX_STR_SIZE);
|
||||||
|
|
||||||
|
size_t sz = 0;
|
||||||
|
char delim = ' ';
|
||||||
|
|
||||||
|
if (cursor < strlen(self->source)
|
||||||
|
&& (self->source[cursor] == '\''
|
||||||
|
|| self->source[cursor] == '"'))
|
||||||
|
{
|
||||||
|
delim = self->source[cursor];
|
||||||
|
cursor++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int found = 0;
|
||||||
|
|
||||||
|
while (cursor < strlen(self->source))
|
||||||
|
{
|
||||||
|
if (self->source[cursor] == delim
|
||||||
|
&& (cursor == 0 || self->source[cursor - 1] != '\\'))
|
||||||
|
{
|
||||||
|
found = 1;
|
||||||
|
cursor++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
value[sz++] = self->source[cursor++];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (found)
|
||||||
|
{
|
||||||
|
info->position = cursor;
|
||||||
|
info->type = NODE_STRING;
|
||||||
|
memcpy(info->value, value, GUX_STR_SIZE * sizeof(char));
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void lexer_add_tok(struct lexer* self,
|
void lexer_add_tok(struct lexer* self,
|
||||||
char* sym,
|
char* sym,
|
||||||
enum NodeType type,
|
enum NodeType type,
|
||||||
|
|
|
@ -21,7 +21,7 @@ struct lexer {
|
||||||
|
|
||||||
struct token_info {
|
struct token_info {
|
||||||
enum NodeType type;
|
enum NodeType type;
|
||||||
char* value;
|
char value[GUX_STR_SIZE];
|
||||||
size_t position;
|
size_t position;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -43,6 +43,15 @@ int lexer_scan_text(struct lexer* self,
|
||||||
char* text,
|
char* text,
|
||||||
struct token_info* info);
|
struct token_info* info);
|
||||||
|
|
||||||
|
int lexer_scan_int(struct lexer* self,
|
||||||
|
struct token_info* info);
|
||||||
|
|
||||||
|
int lexer_scan_float(struct lexer* self,
|
||||||
|
struct token_info* info);
|
||||||
|
|
||||||
|
int lexer_scan_string(struct lexer* self,
|
||||||
|
struct token_info* info);
|
||||||
|
|
||||||
void lexer_add_tok(struct lexer* self,
|
void lexer_add_tok(struct lexer* self,
|
||||||
char* sym,
|
char* sym,
|
||||||
enum NodeType type,
|
enum NodeType type,
|
||||||
|
|
|
@ -4,11 +4,15 @@
|
||||||
#include <commons.h>
|
#include <commons.h>
|
||||||
#include <vec.h>
|
#include <vec.h>
|
||||||
|
|
||||||
#define NODE_TYPE(G) \
|
#define NODE_TYPE(G) \
|
||||||
G(NODE_ROOT), \
|
G(NODE_ROOT), \
|
||||||
G(NODE_SEMICOLON), \
|
G(NODE_SEMICOLON), \
|
||||||
G(NODE_BOOL), G(NODE_ASSERT), G(NODE_OPAR), \
|
G(NODE_BOOL), G(NODE_ASSERT), G(NODE_OPAR), \
|
||||||
G(NODE_CPAR), G(NODE_AND), G(NODE_OR), G(NODE_NOT)
|
G(NODE_CPAR), G(NODE_AND), G(NODE_OR), G(NODE_NOT), \
|
||||||
|
G(NODE_EQ), G(NODE_NE), G(NODE_INT), G(NODE_FLOAT), \
|
||||||
|
G(NODE_STRING), G(NODE_ADD), G(NODE_SUB), G(NODE_MUL), G(NODE_DIV), \
|
||||||
|
G(NODE_MOD), G(NODE_POW), G(NODE_LT), G(NODE_LE), G(NODE_GT), \
|
||||||
|
G(NODE_GE)
|
||||||
|
|
||||||
GUX_ENUM_H(NodeType, NODE_TYPE);
|
GUX_ENUM_H(NodeType, NODE_TYPE);
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#include "parser.h"
|
#include "parser.h"
|
||||||
#include "commons.h"
|
#include "commons.h"
|
||||||
#include "lexer.h"
|
#include "lexer.h"
|
||||||
|
#include "node.h"
|
||||||
#include "vec.h"
|
#include "vec.h"
|
||||||
|
|
||||||
void parser_init(struct parser* self)
|
void parser_init(struct parser* self)
|
||||||
|
@ -230,14 +231,14 @@ struct node* parser_try_new_or(struct parser* self)
|
||||||
|
|
||||||
struct node* parser_try_new_and(struct parser* self)
|
struct node* parser_try_new_and(struct parser* self)
|
||||||
{
|
{
|
||||||
struct node* lhs = parser_try_new_not(self);
|
struct node* lhs = parser_try_new_eqne(self);
|
||||||
|
|
||||||
while (parser_try_consume(self, NODE_AND) == 0)
|
while (parser_try_consume(self, NODE_AND) == 0)
|
||||||
{
|
{
|
||||||
struct node* node = malloc(sizeof(struct node));
|
struct node* node = malloc(sizeof(struct node));
|
||||||
node_init(node, NODE_AND, "", parser_current_line(self));
|
node_init(node, NODE_AND, "", parser_current_line(self));
|
||||||
|
|
||||||
struct node* rhs = parser_try_new_not(self);
|
struct node* rhs = parser_try_new_eqne(self);
|
||||||
|
|
||||||
node_add_child(node, lhs);
|
node_add_child(node, lhs);
|
||||||
node_add_child(node, rhs);
|
node_add_child(node, rhs);
|
||||||
|
@ -248,6 +249,115 @@ struct node* parser_try_new_and(struct parser* self)
|
||||||
return lhs;
|
return lhs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct node* parser_try_new_eqne(struct parser* self)
|
||||||
|
{
|
||||||
|
struct node* lhs = parser_try_new_cmp(self);
|
||||||
|
|
||||||
|
if (parser_type_is(self, NODE_EQ, 0)
|
||||||
|
|| parser_type_is(self, NODE_NE, 0))
|
||||||
|
{
|
||||||
|
struct node* node = malloc(sizeof(struct node));
|
||||||
|
struct node* current = self->tokens.data[self->cursor];
|
||||||
|
self->cursor++;
|
||||||
|
|
||||||
|
node_init(node, current->type, "", parser_current_line(self));
|
||||||
|
|
||||||
|
node_add_child(node, lhs);
|
||||||
|
node_add_child(node, parser_try_new_cmp(self));
|
||||||
|
lhs = node;
|
||||||
|
}
|
||||||
|
|
||||||
|
return lhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct node* parser_try_new_cmp(struct parser* self)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
struct node* lhs = parser_try_new_term(self);
|
||||||
|
|
||||||
|
if (parser_type_is(self, NODE_LT, 0)
|
||||||
|
|| parser_type_is(self, NODE_LE, 0)
|
||||||
|
|| parser_type_is(self, NODE_GT, 0)
|
||||||
|
|| parser_type_is(self, NODE_GE, 0))
|
||||||
|
{
|
||||||
|
struct node* node = malloc(sizeof(struct node));
|
||||||
|
struct node* current = self->tokens.data[self->cursor];
|
||||||
|
self->cursor++;
|
||||||
|
|
||||||
|
node_init(node, current->type, "", parser_current_line(self));
|
||||||
|
|
||||||
|
node_add_child(node, lhs);
|
||||||
|
node_add_child(node, parser_try_new_term(self));
|
||||||
|
lhs = node;
|
||||||
|
}
|
||||||
|
|
||||||
|
return lhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct node* parser_try_new_term(struct parser* self)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
struct node* lhs = parser_try_new_factor(self);
|
||||||
|
|
||||||
|
while (parser_type_is(self, NODE_ADD, 0)
|
||||||
|
|| parser_type_is(self, NODE_SUB, 0))
|
||||||
|
{
|
||||||
|
struct node* node = malloc(sizeof(struct node));
|
||||||
|
struct node* tok = self->tokens.data[self->cursor];
|
||||||
|
node_init(node, tok->type, "", parser_current_line(self));
|
||||||
|
self->cursor++;
|
||||||
|
|
||||||
|
node_add_child(node, lhs);
|
||||||
|
node_add_child(node, parser_try_new_factor(self));
|
||||||
|
lhs = node;
|
||||||
|
}
|
||||||
|
|
||||||
|
return lhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct node* parser_try_new_factor(struct parser* self)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
struct node* lhs = parser_try_new_pow(self);
|
||||||
|
|
||||||
|
while (parser_type_is(self, NODE_MUL, 0)
|
||||||
|
|| parser_type_is(self, NODE_DIV, 0)
|
||||||
|
|| parser_type_is(self, NODE_MOD, 0))
|
||||||
|
{
|
||||||
|
struct node* node = malloc(sizeof(struct node));
|
||||||
|
struct node* tok = self->tokens.data[self->cursor];
|
||||||
|
node_init(node, tok->type, "", parser_current_line(self));
|
||||||
|
self->cursor++;
|
||||||
|
|
||||||
|
node_add_child(node, lhs);
|
||||||
|
node_add_child(node, parser_try_new_pow(self));
|
||||||
|
lhs = node;
|
||||||
|
}
|
||||||
|
|
||||||
|
return lhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct node* parser_try_new_pow(struct parser* self)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
struct node* lhs = parser_try_new_not(self);
|
||||||
|
|
||||||
|
if (parser_try_consume(self, NODE_POW) == 0)
|
||||||
|
{
|
||||||
|
struct node* node = malloc(sizeof(struct node));
|
||||||
|
node_init(node, NODE_POW, "", parser_current_line(self));
|
||||||
|
|
||||||
|
node_add_child(node, lhs);
|
||||||
|
|
||||||
|
node_add_child(node, parser_try_new_not(self));
|
||||||
|
|
||||||
|
lhs = node;
|
||||||
|
}
|
||||||
|
|
||||||
|
return lhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
struct node* parser_try_new_not(struct parser* self)
|
struct node* parser_try_new_not(struct parser* self)
|
||||||
{
|
{
|
||||||
if (parser_try_consume(self, NODE_NOT) == 0)
|
if (parser_try_consume(self, NODE_NOT) == 0)
|
||||||
|
@ -285,7 +395,25 @@ struct node* parser_try_new_builtin(struct parser* self)
|
||||||
{
|
{
|
||||||
assert(self);
|
assert(self);
|
||||||
|
|
||||||
struct node* value = parser_try_consume_new(self, NODE_BOOL);
|
if (parser_type_is(self, NODE_BOOL, 0))
|
||||||
|
{
|
||||||
|
return parser_try_consume_new(self, NODE_BOOL);
|
||||||
|
}
|
||||||
|
|
||||||
return value;
|
if (parser_type_is(self, NODE_INT, 0))
|
||||||
|
{
|
||||||
|
return parser_try_consume_new(self, NODE_INT);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parser_type_is(self, NODE_FLOAT, 0))
|
||||||
|
{
|
||||||
|
return parser_try_consume_new(self, NODE_FLOAT);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parser_type_is(self, NODE_STRING, 0))
|
||||||
|
{
|
||||||
|
return parser_try_consume_new(self, NODE_STRING);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,11 @@ struct node* parser_try_new_instr(struct parser* self);
|
||||||
struct node* parser_try_new_expr(struct parser* self);
|
struct node* parser_try_new_expr(struct parser* self);
|
||||||
struct node* parser_try_new_or(struct parser* self);
|
struct node* parser_try_new_or(struct parser* self);
|
||||||
struct node* parser_try_new_and(struct parser* self);
|
struct node* parser_try_new_and(struct parser* self);
|
||||||
|
struct node* parser_try_new_eqne(struct parser* self);
|
||||||
|
struct node* parser_try_new_cmp(struct parser* self);
|
||||||
|
struct node* parser_try_new_term(struct parser* self);
|
||||||
|
struct node* parser_try_new_factor(struct parser* self);
|
||||||
|
struct node* parser_try_new_pow(struct parser* self);
|
||||||
struct node* parser_try_new_not(struct parser* self);
|
struct node* parser_try_new_not(struct parser* self);
|
||||||
struct node* parser_try_new_literal(struct parser* self);
|
struct node* parser_try_new_literal(struct parser* self);
|
||||||
struct node* parser_try_new_builtin(struct parser* self);
|
struct node* parser_try_new_builtin(struct parser* self);
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#include "type.h"
|
#include "type.h"
|
||||||
|
#include "commons.h"
|
||||||
#include "vec.h"
|
#include "vec.h"
|
||||||
|
|
||||||
GUX_ENUM_C(TypeKind, TYPE_KIND);
|
GUX_ENUM_C(TypeKind, TYPE_KIND);
|
||||||
|
@ -27,7 +28,13 @@ void type_free(struct type* self)
|
||||||
void type_clear(struct type* self)
|
void type_clear(struct type* self)
|
||||||
{
|
{
|
||||||
assert(self);
|
assert(self);
|
||||||
vec_clear(&self->subtypes);
|
|
||||||
|
while (self->subtypes.size > 0)
|
||||||
|
{
|
||||||
|
void* element = vec_pop(&self->subtypes);
|
||||||
|
type_free(element);
|
||||||
|
free(element);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int type_equals(struct type* self, struct type* rhs)
|
int type_equals(struct type* self, struct type* rhs)
|
||||||
|
@ -89,3 +96,15 @@ size_t type_str(struct type* self, char* buffer, size_t size)
|
||||||
|
|
||||||
return sz;
|
return sz;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int type_is(struct type* self, char const* repr)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
assert(repr);
|
||||||
|
|
||||||
|
char buffer[GUX_STR_SIZE];
|
||||||
|
type_str(self, buffer, GUX_STR_SIZE);
|
||||||
|
|
||||||
|
int result = strcmp(repr, buffer) == 0;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
|
@ -5,7 +5,8 @@
|
||||||
#include <vec.h>
|
#include <vec.h>
|
||||||
|
|
||||||
#define TYPE_KIND(G) \
|
#define TYPE_KIND(G) \
|
||||||
G(TYPE_VOID), G(TYPE_BOOL)
|
G(TYPE_VOID), G(TYPE_BOOL), G(TYPE_INT), \
|
||||||
|
G(TYPE_FLOAT), G(TYPE_STRING)
|
||||||
|
|
||||||
GUX_ENUM_H(TypeKind, TYPE_KIND);
|
GUX_ENUM_H(TypeKind, TYPE_KIND);
|
||||||
|
|
||||||
|
@ -22,6 +23,6 @@ void type_clear(struct type* self);
|
||||||
int type_equals(struct type* self, struct type* rhs);
|
int type_equals(struct type* self, struct type* rhs);
|
||||||
void type_add_subtype(struct type* self, enum TypeKind kind);
|
void type_add_subtype(struct type* self, enum TypeKind kind);
|
||||||
size_t type_str(struct type* self, char* buffer, size_t size);
|
size_t type_str(struct type* self, char* buffer, size_t size);
|
||||||
|
int type_is(struct type* self, char const* repr);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -2,11 +2,13 @@
|
||||||
#include "commons.h"
|
#include "commons.h"
|
||||||
#include "node.h"
|
#include "node.h"
|
||||||
#include "type.h"
|
#include "type.h"
|
||||||
|
#include "type_resolver.h"
|
||||||
|
|
||||||
void type_checker_init(struct type_checker* self)
|
void type_checker_init(struct type_checker* self)
|
||||||
{
|
{
|
||||||
assert(self);
|
assert(self);
|
||||||
type_resolver_init(&self->resolver);
|
type_resolver_init(&self->resolver);
|
||||||
|
memset(self->error_msg, 0, GUX_STR_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
void type_checker_free(struct type_checker* self)
|
void type_checker_free(struct type_checker* self)
|
||||||
|
@ -23,10 +25,91 @@ int type_checker_check(struct type_checker* self,
|
||||||
|
|
||||||
switch (node->type)
|
switch (node->type)
|
||||||
{
|
{
|
||||||
|
case NODE_LT:
|
||||||
|
case NODE_LE:
|
||||||
|
case NODE_GT:
|
||||||
|
case NODE_GE: {
|
||||||
|
struct node* lhs = node->children.data[0];
|
||||||
|
struct node* rhs = node->children.data[1];
|
||||||
|
|
||||||
|
struct type lhs_type;
|
||||||
|
type_init(&lhs_type, TYPE_VOID);
|
||||||
|
int solve_lhs = type_resolver_resolve(&self->resolver, lhs, &lhs_type);
|
||||||
|
|
||||||
|
struct type rhs_type;
|
||||||
|
type_init(&rhs_type, TYPE_VOID);
|
||||||
|
int solve_rhs = type_resolver_resolve(&self->resolver, rhs, &rhs_type);
|
||||||
|
|
||||||
|
size_t MSG_SZ = GUX_STR_SIZE * 4;
|
||||||
|
char lhs_msg[GUX_STR_SIZE];
|
||||||
|
type_str(&lhs_type, lhs_msg, GUX_STR_SIZE);
|
||||||
|
char rhs_msg[GUX_STR_SIZE];
|
||||||
|
type_str(&rhs_type, rhs_msg, GUX_STR_SIZE);
|
||||||
|
self->error_line = lhs->line;
|
||||||
|
|
||||||
|
if (lhs_type.kind != TYPE_INT && lhs_type.kind != TYPE_FLOAT)
|
||||||
|
{
|
||||||
|
if (rhs_type.kind == TYPE_INT || rhs_type.kind == TYPE_FLOAT)
|
||||||
|
{
|
||||||
|
snprintf(self->error_msg, MSG_SZ,
|
||||||
|
"wrong type: expected <%s>, got <%s>",
|
||||||
|
rhs_msg,
|
||||||
|
lhs_msg);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
snprintf(self->error_msg, MSG_SZ,
|
||||||
|
"wrong type: expected <INT> or <FLOAT>, got <%s>",
|
||||||
|
lhs_msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (rhs_type.kind != TYPE_INT && rhs_type.kind != TYPE_FLOAT)
|
||||||
|
{
|
||||||
|
if (lhs_type.kind == TYPE_INT || lhs_type.kind == TYPE_FLOAT)
|
||||||
|
{
|
||||||
|
snprintf(self->error_msg, MSG_SZ,
|
||||||
|
"wrong type: expected <%s>, got <%s>",
|
||||||
|
lhs_msg,
|
||||||
|
rhs_msg);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
snprintf(self->error_msg, MSG_SZ,
|
||||||
|
"wrong type: expected <INT> or <FLOAT>, got <%s>",
|
||||||
|
lhs_msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (!type_equals(&lhs_type, &rhs_type))
|
||||||
|
{
|
||||||
|
snprintf(self->error_msg, MSG_SZ,
|
||||||
|
"type mismatch (<%s>, <%s>)",
|
||||||
|
lhs_msg,
|
||||||
|
rhs_msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (solve_lhs != 0
|
||||||
|
|| solve_rhs != 0
|
||||||
|
|| !type_equals(&lhs_type, &rhs_type)
|
||||||
|
|| lhs_type.subtypes.size > 0
|
||||||
|
|| rhs_type.subtypes.size > 0
|
||||||
|
|| (lhs_type.kind != TYPE_INT && lhs_type.kind != TYPE_FLOAT)
|
||||||
|
|| (rhs_type.kind != TYPE_INT && rhs_type.kind != TYPE_FLOAT))
|
||||||
|
{
|
||||||
|
type_free(&lhs_type);
|
||||||
|
type_free(&rhs_type);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
type_free(&lhs_type);
|
||||||
|
type_free(&rhs_type);
|
||||||
|
|
||||||
|
} return 0;
|
||||||
|
|
||||||
case NODE_ASSERT:
|
case NODE_ASSERT:
|
||||||
case NODE_NOT:
|
case NODE_NOT:
|
||||||
case NODE_AND:
|
case NODE_AND:
|
||||||
case NODE_OR:{
|
case NODE_OR: {
|
||||||
for (size_t i=0; i<node->children.size; i++)
|
for (size_t i=0; i<node->children.size; i++)
|
||||||
{
|
{
|
||||||
if (type_checker_ensure_kind(self,
|
if (type_checker_ensure_kind(self,
|
||||||
|
@ -38,6 +121,55 @@ int type_checker_check(struct type_checker* self,
|
||||||
}
|
}
|
||||||
} return 0;
|
} return 0;
|
||||||
|
|
||||||
|
case NODE_EQ:
|
||||||
|
case NODE_NE: {
|
||||||
|
struct type type;
|
||||||
|
type_init(&type, TYPE_VOID);
|
||||||
|
type_resolver_resolve(&self->resolver, node->children.data[0], &type);
|
||||||
|
|
||||||
|
for (size_t i=1; i<node->children.size; i++)
|
||||||
|
{
|
||||||
|
struct type node_type;
|
||||||
|
type_init(&node_type, TYPE_VOID);
|
||||||
|
|
||||||
|
type_resolver_resolve(&self->resolver,
|
||||||
|
node->children.data[i],
|
||||||
|
&node_type);
|
||||||
|
|
||||||
|
if (!type_equals(&type, &node_type))
|
||||||
|
{
|
||||||
|
self->error_line = node->line;
|
||||||
|
snprintf(self->error_msg,
|
||||||
|
GUX_STR_SIZE, "cannot compare different types");
|
||||||
|
type_free(&type);
|
||||||
|
type_free(&node_type);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
type_free(&node_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
type_free(&type);
|
||||||
|
} return 0;
|
||||||
|
|
||||||
|
case NODE_ADD:
|
||||||
|
case NODE_SUB:
|
||||||
|
case NODE_MUL:
|
||||||
|
case NODE_DIV:
|
||||||
|
case NODE_MOD:
|
||||||
|
case NODE_POW: {
|
||||||
|
struct type type;
|
||||||
|
type_init(&type, TYPE_VOID);
|
||||||
|
|
||||||
|
if (type_resolver_resolve(&self->resolver, node, &type) != 0)
|
||||||
|
{
|
||||||
|
type_free(&type);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
type_free(&type);
|
||||||
|
} return 0;
|
||||||
|
|
||||||
default: {
|
default: {
|
||||||
for (size_t i=0; i<node->children.size; i++)
|
for (size_t i=0; i<node->children.size; i++)
|
||||||
{
|
{
|
||||||
|
@ -66,6 +198,7 @@ int type_checker_ensure_kind(struct type_checker* self,
|
||||||
if (type.kind != kind || type.subtypes.size != 0)
|
if (type.kind != kind || type.subtypes.size != 0)
|
||||||
{
|
{
|
||||||
self->error_line = node->line;
|
self->error_line = node->line;
|
||||||
|
|
||||||
snprintf(self->error_msg, GUX_STR_SIZE,
|
snprintf(self->error_msg, GUX_STR_SIZE,
|
||||||
"type mismatch: expected <%s>, got <%s>",
|
"type mismatch: expected <%s>, got <%s>",
|
||||||
TypeKindStr[kind] + strlen("TYPE_"),
|
TypeKindStr[kind] + strlen("TYPE_"),
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
struct type_checker {
|
struct type_checker {
|
||||||
struct type_resolver resolver;
|
struct type_resolver resolver;
|
||||||
char* error_msg;
|
char error_msg[GUX_STR_SIZE];
|
||||||
int error_line;
|
int error_line;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -24,14 +24,81 @@ int type_resolver_resolve(struct type_resolver* self,
|
||||||
|
|
||||||
switch (node->type)
|
switch (node->type)
|
||||||
{
|
{
|
||||||
|
case NODE_EQ:
|
||||||
|
case NODE_NE:
|
||||||
case NODE_ASSERT:
|
case NODE_ASSERT:
|
||||||
case NODE_BOOL:
|
case NODE_BOOL:
|
||||||
case NODE_AND:
|
case NODE_AND:
|
||||||
case NODE_OR:
|
case NODE_OR:
|
||||||
case NODE_NOT:{
|
case NODE_NOT:
|
||||||
|
case NODE_LT:
|
||||||
|
case NODE_LE:
|
||||||
|
case NODE_GT:
|
||||||
|
case NODE_GE:{
|
||||||
type->kind = TYPE_BOOL;
|
type->kind = TYPE_BOOL;
|
||||||
} return 0;
|
} return 0;
|
||||||
|
|
||||||
|
case NODE_INT: {
|
||||||
|
type->kind = TYPE_INT;
|
||||||
|
} return 0;
|
||||||
|
|
||||||
|
case NODE_FLOAT: {
|
||||||
|
type->kind = TYPE_FLOAT;
|
||||||
|
} return 0;
|
||||||
|
|
||||||
|
case NODE_STRING: {
|
||||||
|
type->kind = TYPE_STRING;
|
||||||
|
} return 0;
|
||||||
|
|
||||||
|
case NODE_ADD:
|
||||||
|
case NODE_SUB:
|
||||||
|
case NODE_MUL:
|
||||||
|
case NODE_DIV:
|
||||||
|
case NODE_MOD:
|
||||||
|
case NODE_POW:{
|
||||||
|
struct type lhs;
|
||||||
|
type_init(&lhs, TYPE_VOID);
|
||||||
|
type_resolver_resolve(self, node->children.data[0], &lhs);
|
||||||
|
|
||||||
|
struct type rhs;
|
||||||
|
type_init(&rhs, TYPE_VOID);
|
||||||
|
type_resolver_resolve(self, node->children.data[1], &rhs);
|
||||||
|
|
||||||
|
int equals = type_equals(&lhs, &rhs);
|
||||||
|
|
||||||
|
if (type_is(&lhs, "INT") && equals)
|
||||||
|
{
|
||||||
|
type->kind = TYPE_INT;
|
||||||
|
}
|
||||||
|
else if (type_is(&lhs, "FLOAT") && equals)
|
||||||
|
{
|
||||||
|
type->kind = TYPE_FLOAT;
|
||||||
|
}
|
||||||
|
else if (type_is(&lhs, "STRING") && equals && node->type == NODE_ADD)
|
||||||
|
{
|
||||||
|
type->kind = TYPE_STRING;
|
||||||
|
}
|
||||||
|
else if (type_is(&lhs, "STRING")
|
||||||
|
&& type_is(&rhs, "INT") && node->type == NODE_MUL)
|
||||||
|
{
|
||||||
|
type->kind = TYPE_STRING;
|
||||||
|
}
|
||||||
|
else if (type_is(&lhs, "INT")
|
||||||
|
&& type_is(&rhs, "STRING") && node->type == NODE_MUL)
|
||||||
|
{
|
||||||
|
type->kind = TYPE_STRING;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
type_free(&lhs);
|
||||||
|
type_free(&rhs);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
type_free(&lhs);
|
||||||
|
type_free(&rhs);
|
||||||
|
} return 0;
|
||||||
|
|
||||||
default: {
|
default: {
|
||||||
} return 1;
|
} return 1;
|
||||||
}
|
}
|
||||||
|
|
324
lang/src/value.c
324
lang/src/value.c
|
@ -9,9 +9,41 @@ void value_init_bool(struct value* self, int value, int line)
|
||||||
self->line = line;
|
self->line = line;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void value_init_int(struct value* self, int value, int line)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
|
||||||
|
type_init(&self->type, TYPE_INT);
|
||||||
|
self->data.i = value;
|
||||||
|
self->line = line;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void value_init_float(struct value* self, float value, int line)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
|
||||||
|
type_init(&self->type, TYPE_FLOAT);
|
||||||
|
self->data.f = value;
|
||||||
|
self->line = line;
|
||||||
|
}
|
||||||
|
|
||||||
|
void value_init_string(struct value* self, char* value, int line)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
|
||||||
|
type_init(&self->type, TYPE_STRING);
|
||||||
|
self->data.s = strdup(value);
|
||||||
|
self->line = line;
|
||||||
|
}
|
||||||
|
|
||||||
void value_free(struct value* self)
|
void value_free(struct value* self)
|
||||||
{
|
{
|
||||||
assert(self);
|
assert(self);
|
||||||
|
if (self->type.kind == TYPE_STRING)
|
||||||
|
{
|
||||||
|
free(self->data.s);
|
||||||
|
}
|
||||||
type_free(&self->type);
|
type_free(&self->type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,6 +65,22 @@ size_t value_str(struct value* self, char* buffer, size_t size)
|
||||||
sz += snprintf(buffer + sz, size - sz, "%s",
|
sz += snprintf(buffer + sz, size - sz, "%s",
|
||||||
self->data.b ? "true" : "false");
|
self->data.b ? "true" : "false");
|
||||||
}
|
}
|
||||||
|
else if (self->type.kind == TYPE_INT)
|
||||||
|
{
|
||||||
|
sz += snprintf(buffer + sz, size - sz, "%d", self->data.i);
|
||||||
|
}
|
||||||
|
else if (self->type.kind == TYPE_FLOAT)
|
||||||
|
{
|
||||||
|
sz += snprintf(buffer + sz, size - sz, "%f", self->data.f);
|
||||||
|
}
|
||||||
|
else if (self->type.kind == TYPE_STRING)
|
||||||
|
{
|
||||||
|
sz += snprintf(buffer + sz, size - sz, "%s", self->data.s);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
assert(0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return sz;
|
return sz;
|
||||||
|
@ -79,3 +127,279 @@ struct value* value_try_new_not(struct value* self)
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct value* value_try_new_add(struct value* self, struct value* rhs)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
assert(rhs);
|
||||||
|
|
||||||
|
struct value* res = malloc(sizeof(struct value));
|
||||||
|
|
||||||
|
if (type_equals(&self->type, &rhs->type) && type_is(&self->type, "INT"))
|
||||||
|
{
|
||||||
|
value_init_int(res, self->data.i + rhs->data.i, self->line);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type_equals(&self->type, &rhs->type) && type_is(&self->type, "FLOAT"))
|
||||||
|
{
|
||||||
|
value_init_float(res, self->data.f + rhs->data.f, self->line);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type_equals(&self->type, &rhs->type) && type_is(&self->type, "STRING"))
|
||||||
|
{
|
||||||
|
size_t const SZ = strlen(self->data.s) + strlen(rhs->data.s) + 1;
|
||||||
|
char str[SZ];
|
||||||
|
snprintf(str, SZ, "%s%s", self->data.s, rhs->data.s);
|
||||||
|
value_init_string(res, str, self->line);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(res);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct value* value_try_new_sub(struct value* self, struct value* rhs)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
assert(rhs);
|
||||||
|
|
||||||
|
struct value* res = malloc(sizeof(struct value));
|
||||||
|
|
||||||
|
if (type_equals(&self->type, &rhs->type) && type_is(&self->type, "INT"))
|
||||||
|
{
|
||||||
|
value_init_int(res, self->data.i - rhs->data.i, self->line);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type_equals(&self->type, &rhs->type) && type_is(&self->type, "FLOAT"))
|
||||||
|
{
|
||||||
|
value_init_float(res, self->data.f - rhs->data.f, self->line);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(res);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct value* value_try_new_mul(struct value* self, struct value* rhs)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
assert(rhs);
|
||||||
|
|
||||||
|
struct value* res = malloc(sizeof(struct value));
|
||||||
|
|
||||||
|
if (type_equals(&self->type, &rhs->type) && type_is(&self->type, "INT"))
|
||||||
|
{
|
||||||
|
value_init_int(res, self->data.i * rhs->data.i, self->line);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type_equals(&self->type, &rhs->type) && type_is(&self->type, "FLOAT"))
|
||||||
|
{
|
||||||
|
value_init_float(res, self->data.f * rhs->data.f, self->line);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct value* factor_val = self;
|
||||||
|
struct value* content_val = rhs;
|
||||||
|
|
||||||
|
if (type_is(&factor_val->type, "STRING") && type_is(&content_val->type, "INT"))
|
||||||
|
{
|
||||||
|
struct value* tmp = factor_val;
|
||||||
|
factor_val = content_val;
|
||||||
|
content_val = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type_is(&factor_val->type, "INT") && type_is(&content_val->type, "STRING"))
|
||||||
|
{
|
||||||
|
int factor = factor_val->data.i;
|
||||||
|
char* content = content_val->data.s;
|
||||||
|
|
||||||
|
size_t const SZ = factor * strlen(content);
|
||||||
|
char str[SZ];
|
||||||
|
|
||||||
|
for (int i=0; i<factor; i++)
|
||||||
|
{
|
||||||
|
snprintf(str + i * strlen(content), SZ, "%s", content);
|
||||||
|
}
|
||||||
|
|
||||||
|
value_init_string(res, str, factor_val->line);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(res);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct value* value_try_new_div(struct value* self, struct value* rhs)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
assert(rhs);
|
||||||
|
|
||||||
|
struct value* res = malloc(sizeof(struct value));
|
||||||
|
|
||||||
|
if (type_equals(&self->type, &rhs->type) && type_is(&self->type, "INT"))
|
||||||
|
{
|
||||||
|
value_init_int(res, self->data.i / rhs->data.i, self->line);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type_equals(&self->type, &rhs->type) && type_is(&self->type, "FLOAT"))
|
||||||
|
{
|
||||||
|
value_init_float(res, self->data.f / rhs->data.f, self->line);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(res);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct value* value_try_new_mod(struct value* self, struct value* rhs)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
assert(rhs);
|
||||||
|
|
||||||
|
struct value* res = malloc(sizeof(struct value));
|
||||||
|
|
||||||
|
if (type_equals(&self->type, &rhs->type) && type_is(&self->type, "INT"))
|
||||||
|
{
|
||||||
|
value_init_int(res, self->data.i % rhs->data.i, self->line);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type_equals(&self->type, &rhs->type) && type_is(&self->type, "FLOAT"))
|
||||||
|
{
|
||||||
|
value_init_float(res, fmod(self->data.f, rhs->data.f), self->line);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(res);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct value* value_try_new_pow(struct value* self, struct value* rhs)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
assert(rhs);
|
||||||
|
|
||||||
|
struct value* res = malloc(sizeof(struct value));
|
||||||
|
|
||||||
|
if (type_equals(&self->type, &rhs->type) && type_is(&self->type, "INT"))
|
||||||
|
{
|
||||||
|
value_init_int(res, pow(self->data.i, rhs->data.i), self->line);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type_equals(&self->type, &rhs->type) && type_is(&self->type, "FLOAT"))
|
||||||
|
{
|
||||||
|
value_init_float(res, powf(self->data.f, rhs->data.f), self->line);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(res);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct value* value_try_new_lt(struct value* self, struct value* rhs)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
assert(rhs);
|
||||||
|
|
||||||
|
struct value* res = malloc(sizeof(struct value));
|
||||||
|
|
||||||
|
if (type_equals(&self->type, &rhs->type) && type_is(&self->type, "INT"))
|
||||||
|
{
|
||||||
|
value_init_bool(res, self->data.i < rhs->data.i, self->line);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type_equals(&self->type, &rhs->type) && type_is(&self->type, "FLOAT"))
|
||||||
|
{
|
||||||
|
value_init_bool(res, self->data.f < rhs->data.f, self->line);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(res);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct value* value_try_new_le(struct value* self, struct value* rhs)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
assert(rhs);
|
||||||
|
|
||||||
|
struct value* res = malloc(sizeof(struct value));
|
||||||
|
|
||||||
|
if (type_equals(&self->type, &rhs->type) && type_is(&self->type, "INT"))
|
||||||
|
{
|
||||||
|
value_init_bool(res, self->data.i <= rhs->data.i, self->line);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type_equals(&self->type, &rhs->type) && type_is(&self->type, "FLOAT"))
|
||||||
|
{
|
||||||
|
value_init_bool(res, self->data.f <= rhs->data.f, self->line);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(res);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct value* value_try_new_gt(struct value* self, struct value* rhs)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
assert(rhs);
|
||||||
|
|
||||||
|
struct value* res = malloc(sizeof(struct value));
|
||||||
|
|
||||||
|
if (type_equals(&self->type, &rhs->type) && type_is(&self->type, "INT"))
|
||||||
|
{
|
||||||
|
value_init_bool(res, self->data.i > rhs->data.i, self->line);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type_equals(&self->type, &rhs->type) && type_is(&self->type, "FLOAT"))
|
||||||
|
{
|
||||||
|
value_init_bool(res, self->data.f > rhs->data.f, self->line);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(res);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct value* value_try_new_ge(struct value* self, struct value* rhs)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
assert(rhs);
|
||||||
|
|
||||||
|
struct value* res = malloc(sizeof(struct value));
|
||||||
|
|
||||||
|
if (type_equals(&self->type, &rhs->type) && type_is(&self->type, "INT"))
|
||||||
|
{
|
||||||
|
value_init_bool(res, self->data.i >= rhs->data.i, self->line);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type_equals(&self->type, &rhs->type) && type_is(&self->type, "FLOAT"))
|
||||||
|
{
|
||||||
|
value_init_bool(res, self->data.f >= rhs->data.f, self->line);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(res);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
|
@ -9,6 +9,9 @@
|
||||||
|
|
||||||
union value_data {
|
union value_data {
|
||||||
int b;
|
int b;
|
||||||
|
char* s;
|
||||||
|
float f;
|
||||||
|
int i;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct value {
|
struct value {
|
||||||
|
@ -18,6 +21,10 @@ struct value {
|
||||||
};
|
};
|
||||||
|
|
||||||
void value_init_bool(struct value* self, int value, int line);
|
void value_init_bool(struct value* self, int value, int line);
|
||||||
|
void value_init_int(struct value* self, int value, int line);
|
||||||
|
void value_init_float(struct value* self, float value, int line);
|
||||||
|
void value_init_string(struct value* self, char* value, int line);
|
||||||
|
|
||||||
void value_free(struct value* self);
|
void value_free(struct value* self);
|
||||||
|
|
||||||
size_t value_str(struct value* self, char* buffer, size_t size);
|
size_t value_str(struct value* self, char* buffer, size_t size);
|
||||||
|
@ -26,4 +33,16 @@ struct value* value_try_new_and(struct value* self, struct value* rhs);
|
||||||
struct value* value_try_new_or(struct value* self, struct value* rhs);
|
struct value* value_try_new_or(struct value* self, struct value* rhs);
|
||||||
struct value* value_try_new_not(struct value* self);
|
struct value* value_try_new_not(struct value* self);
|
||||||
|
|
||||||
|
struct value* value_try_new_add(struct value* self, struct value* rhs);
|
||||||
|
struct value* value_try_new_sub(struct value* self, struct value* rhs);
|
||||||
|
struct value* value_try_new_mul(struct value* self, struct value* rhs);
|
||||||
|
struct value* value_try_new_div(struct value* self, struct value* rhs);
|
||||||
|
struct value* value_try_new_mod(struct value* self, struct value* rhs);
|
||||||
|
struct value* value_try_new_pow(struct value* self, struct value* rhs);
|
||||||
|
|
||||||
|
struct value* value_try_new_lt(struct value* self, struct value* rhs);
|
||||||
|
struct value* value_try_new_le(struct value* self, struct value* rhs);
|
||||||
|
struct value* value_try_new_gt(struct value* self, struct value* rhs);
|
||||||
|
struct value* value_try_new_ge(struct value* self, struct value* rhs);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -14,7 +14,7 @@ static void test_lexer(char const* source, size_t size, ...)
|
||||||
struct lexer lex;
|
struct lexer lex;
|
||||||
lexer_init(&lex, source);
|
lexer_init(&lex, source);
|
||||||
int err = lexer_extract(&lex, &tokens);
|
int err = lexer_extract(&lex, &tokens);
|
||||||
cr_assert_eq(0, err);
|
cr_assert_eq(0, err, "%s", lex.error_msg);
|
||||||
cr_assert_eq(size, tokens.size,
|
cr_assert_eq(size, tokens.size,
|
||||||
"size (%zu) != tokens.size (%zu)",
|
"size (%zu) != tokens.size (%zu)",
|
||||||
size, tokens.size);
|
size, tokens.size);
|
||||||
|
@ -68,3 +68,42 @@ Test(lexer, comment) {
|
||||||
Test(lexer, bool_arith) {
|
Test(lexer, bool_arith) {
|
||||||
test_lexer("&&||!", 3, "AND", "OR", "NOT");
|
test_lexer("&&||!", 3, "AND", "OR", "NOT");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Test(lexer, eqne) {
|
||||||
|
test_lexer("!= ==", 2, "NE", "EQ");
|
||||||
|
}
|
||||||
|
|
||||||
|
Test(lexer, int) {
|
||||||
|
test_lexer("5 -27", 2, "INT[5]", "INT[-27]");
|
||||||
|
}
|
||||||
|
|
||||||
|
Test(lexer, float) {
|
||||||
|
test_lexer("0.5 -2.7", 2, "FLOAT[0.5]", "FLOAT[-2.7]");
|
||||||
|
}
|
||||||
|
|
||||||
|
Test(lexer, string) {
|
||||||
|
test_lexer("'hello world ' \" bim\" '\\\"hello\\\"'", 3,
|
||||||
|
"STRING[hello world ]",
|
||||||
|
"STRING[ bim]",
|
||||||
|
"STRING[\\\"hello\\\"]");
|
||||||
|
}
|
||||||
|
|
||||||
|
Test(lexer, arithemtic) {
|
||||||
|
test_lexer("+-*/%**", 6,
|
||||||
|
"ADD",
|
||||||
|
"SUB",
|
||||||
|
"MUL",
|
||||||
|
"DIV",
|
||||||
|
"MOD",
|
||||||
|
"POW");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Test(lexer, comparisons) {
|
||||||
|
test_lexer(">>=<<=", 4,
|
||||||
|
"GT",
|
||||||
|
"GE",
|
||||||
|
"LT",
|
||||||
|
"LE");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -67,3 +67,44 @@ Test(parser, bool_arith) {
|
||||||
"true && !false || true;");
|
"true && !false || true;");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Test(parser, eqne) {
|
||||||
|
test_parser("ROOT(EQ(BOOL[true],BOOL[false]))",
|
||||||
|
"true == false;");
|
||||||
|
|
||||||
|
test_parser("ROOT(NE(BOOL[true],BOOL[false]))",
|
||||||
|
"true != false;");
|
||||||
|
}
|
||||||
|
|
||||||
|
Test(parser, num) {
|
||||||
|
test_parser("ROOT(EQ(INT[3],INT[-7]))",
|
||||||
|
"3 == -7;");
|
||||||
|
|
||||||
|
test_parser("ROOT(NE(FLOAT[2.9],FLOAT[-3.]))",
|
||||||
|
"2.9 != -3.;");
|
||||||
|
|
||||||
|
test_parser("ROOT(NE(STRING[bim],STRING[bam]))",
|
||||||
|
"'bim' != 'bam';");
|
||||||
|
}
|
||||||
|
|
||||||
|
Test(parser, arithmetic) {
|
||||||
|
test_parser("ROOT(ADD(INT[1],MUL(INT[2],INT[3])))",
|
||||||
|
"1 + 2 * 3;");
|
||||||
|
|
||||||
|
test_parser("ROOT(MUL(ADD(INT[1],INT[2]),INT[3]))",
|
||||||
|
"(1 + 2) * 3;");
|
||||||
|
|
||||||
|
test_parser("ROOT(SUB(INT[1],DIV(INT[2],INT[3])))",
|
||||||
|
"1 - 2 / 3;");
|
||||||
|
|
||||||
|
test_parser("ROOT(DIV(SUB(INT[1],INT[2]),INT[3]))",
|
||||||
|
"(1 - 2) / 3;");
|
||||||
|
}
|
||||||
|
|
||||||
|
Test(parser, cmp) {
|
||||||
|
test_parser("ROOT(LT(ADD(INT[1],INT[1]),INT[3]))",
|
||||||
|
"1 + 1 < 3;");
|
||||||
|
|
||||||
|
test_parser("ROOT(AND(LE(INT[0],INT[2]),GE(FLOAT[5.0],FLOAT[1.0])))",
|
||||||
|
"0 <= 2 && 5.0 >= 1.0;");
|
||||||
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
#include "commons.h"
|
||||||
#include <criterion/criterion.h>
|
#include <criterion/criterion.h>
|
||||||
#include <parser.h>
|
#include <parser.h>
|
||||||
#include <parser.h>
|
#include <parser.h>
|
||||||
|
@ -14,7 +15,11 @@ static void test_check_ok(char const* source)
|
||||||
struct type_checker tc;
|
struct type_checker tc;
|
||||||
type_checker_init(&tc);
|
type_checker_init(&tc);
|
||||||
|
|
||||||
cr_assert_eq(type_checker_check(&tc, ast), 0);
|
char buf[GUX_STR_SIZE];
|
||||||
|
node_str(ast, buf, GUX_STR_SIZE);
|
||||||
|
|
||||||
|
cr_assert_eq(type_checker_check(&tc, ast), 0,
|
||||||
|
"type checker failed: %s", buf);
|
||||||
|
|
||||||
type_checker_free(&tc);
|
type_checker_free(&tc);
|
||||||
|
|
||||||
|
@ -29,12 +34,12 @@ static void test_check_ko(char const* source)
|
||||||
parser_init(&parser);
|
parser_init(&parser);
|
||||||
|
|
||||||
struct node* ast = parser_try_new_root(&parser, source);
|
struct node* ast = parser_try_new_root(&parser, source);
|
||||||
cr_assert_neq(ast, NULL);
|
cr_assert_neq(ast, NULL, "NULL: %s", source);
|
||||||
|
|
||||||
struct type_checker tc;
|
struct type_checker tc;
|
||||||
type_checker_init(&tc);
|
type_checker_init(&tc);
|
||||||
|
|
||||||
cr_assert_neq(type_checker_check(&tc, ast), 0);
|
cr_assert_neq(type_checker_check(&tc, ast), 0, "%s == null", source);
|
||||||
|
|
||||||
type_checker_free(&tc);
|
type_checker_free(&tc);
|
||||||
|
|
||||||
|
@ -48,4 +53,103 @@ Test(type_checker, booleans) {
|
||||||
test_check_ok("true && false;");
|
test_check_ok("true && false;");
|
||||||
test_check_ok("true || false;");
|
test_check_ok("true || false;");
|
||||||
test_check_ok("true || !false && false;");
|
test_check_ok("true || !false && false;");
|
||||||
|
|
||||||
|
test_check_ko("7 && true;");
|
||||||
|
test_check_ko("false && -1;");
|
||||||
|
test_check_ko("7.0 && true;");
|
||||||
|
test_check_ko("false && -1.0;");
|
||||||
|
test_check_ko("'true' && true;");
|
||||||
|
test_check_ko("false && 'false';");
|
||||||
|
|
||||||
|
test_check_ko("7 || true;");
|
||||||
|
test_check_ko("false || -1;");
|
||||||
|
test_check_ko("7.0 || true;");
|
||||||
|
test_check_ko("false || -1.0;");
|
||||||
|
test_check_ko("'true' || true;");
|
||||||
|
test_check_ko("false || 'false';");
|
||||||
|
|
||||||
|
test_check_ko("!7;");
|
||||||
|
test_check_ko("!7.0;");
|
||||||
|
test_check_ko("!'true';");
|
||||||
|
}
|
||||||
|
|
||||||
|
Test(type_checker, arithmetic) {
|
||||||
|
test_check_ok("1 + 1;");
|
||||||
|
test_check_ok("1.0 + 1.2;");
|
||||||
|
test_check_ko("true + false;");
|
||||||
|
test_check_ok("'aze' + 'popopo';");
|
||||||
|
|
||||||
|
test_check_ko("1 - 'salut';");
|
||||||
|
test_check_ko("1.2 - 15;");
|
||||||
|
test_check_ko("false - 1.2;");
|
||||||
|
test_check_ko("\"a\" - true;");
|
||||||
|
|
||||||
|
test_check_ko("-17 * false;");
|
||||||
|
test_check_ko("-2.3 * 'oo';");
|
||||||
|
test_check_ko("true * 45;");
|
||||||
|
test_check_ko("'1' * 1.02;");
|
||||||
|
|
||||||
|
test_check_ko("441 / 3.1415;");
|
||||||
|
test_check_ko(".9 / false;");
|
||||||
|
test_check_ko("true / 'bim';");
|
||||||
|
test_check_ko("'aze' / 145;");
|
||||||
|
|
||||||
|
test_check_ok("33 % 1;");
|
||||||
|
test_check_ok("3.3 % 1.2;");
|
||||||
|
test_check_ko("false % false;");
|
||||||
|
test_check_ko("'' % 'other';");
|
||||||
|
|
||||||
|
test_check_ok("1 ** -45;");
|
||||||
|
test_check_ok("1.2 ** 0.54;");
|
||||||
|
test_check_ko("true ** false;");
|
||||||
|
test_check_ko("'coucou' ** 'bam';");
|
||||||
|
}
|
||||||
|
|
||||||
|
Test(type_checker, cmp) {
|
||||||
|
test_check_ko("-5 < 3.2;");
|
||||||
|
test_check_ko("-5 < true;");
|
||||||
|
test_check_ko("-5 < 'salut';");
|
||||||
|
test_check_ko("3.2 < 5;");
|
||||||
|
test_check_ko("true < 5;");
|
||||||
|
test_check_ko("\"5\" < 5;");
|
||||||
|
|
||||||
|
test_check_ko("-5 <= 3.2;");
|
||||||
|
test_check_ko("-5 <= true;");
|
||||||
|
test_check_ko("-5 <= 'salut';");
|
||||||
|
test_check_ko("3.2 <= 5;");
|
||||||
|
test_check_ko("true <= 5;");
|
||||||
|
test_check_ko("\"5\" <= 5;");
|
||||||
|
|
||||||
|
test_check_ko("-5 > 3.2;");
|
||||||
|
test_check_ko("-5 > true;");
|
||||||
|
test_check_ko("-5 > 'salut';");
|
||||||
|
test_check_ko("3.2 > 5;");
|
||||||
|
test_check_ko("true > 5;");
|
||||||
|
test_check_ko("\"5\" > 5;");
|
||||||
|
|
||||||
|
test_check_ko("-5 >= 3.2;");
|
||||||
|
test_check_ko("-5 >= true;");
|
||||||
|
test_check_ko("-5 >= 'salut';");
|
||||||
|
test_check_ko("3.2 >= 5;");
|
||||||
|
test_check_ko("true >= 5;");
|
||||||
|
test_check_ko("\"5\" >= 5;");
|
||||||
|
|
||||||
|
test_check_ko("true < true;");
|
||||||
|
test_check_ko("true <= true;");
|
||||||
|
test_check_ko("true > true;");
|
||||||
|
test_check_ko("true >= true;");
|
||||||
|
|
||||||
|
test_check_ko("'hello' < 'hello';");
|
||||||
|
test_check_ko("'hello' <= 'hello';");
|
||||||
|
test_check_ko("'hello' > 'hello';");
|
||||||
|
test_check_ko("'hello' >= 'hello';");
|
||||||
|
|
||||||
|
test_check_ok("5 > 5;");
|
||||||
|
test_check_ok("5.1 > 5.2;");
|
||||||
|
test_check_ok("5 >= 5;");
|
||||||
|
test_check_ok("5.1 >= 5.2;");
|
||||||
|
test_check_ok("5 < 5;");
|
||||||
|
test_check_ok("5.1 < 5.2;");
|
||||||
|
test_check_ok("5 <= 5;");
|
||||||
|
test_check_ok("5.1 <= 5.2;");
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#ifndef COMMONS_H
|
#ifndef COMMONS_H
|
||||||
#define COMMONS_H
|
#define COMMONS_H
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
|
@ -44,16 +44,6 @@ void* vec_pop(struct vec* self)
|
||||||
return element;
|
return element;
|
||||||
}
|
}
|
||||||
|
|
||||||
void vec_clear(struct vec* self)
|
|
||||||
{
|
|
||||||
assert(self);
|
|
||||||
|
|
||||||
while (self->size > 0)
|
|
||||||
{
|
|
||||||
vec_pop(self);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void vec_free_elements(struct vec* self)
|
void vec_free_elements(struct vec* self)
|
||||||
{
|
{
|
||||||
assert(self);
|
assert(self);
|
||||||
|
|
|
@ -14,7 +14,6 @@ void vec_free(struct vec* self);
|
||||||
|
|
||||||
void vec_push(struct vec* self, void* element);
|
void vec_push(struct vec* self, void* element);
|
||||||
void* vec_pop(struct vec* self);
|
void* vec_pop(struct vec* self);
|
||||||
void vec_clear(struct vec* self);
|
|
||||||
|
|
||||||
void vec_free_elements(struct vec* self);
|
void vec_free_elements(struct vec* self);
|
||||||
|
|
||||||
|
|
|
@ -1,14 +0,0 @@
|
||||||
assert true;
|
|
||||||
|
|
||||||
assert !false;
|
|
||||||
assert !!true;
|
|
||||||
|
|
||||||
assert true && true;
|
|
||||||
assert !(true && false);
|
|
||||||
assert !(false && true);
|
|
||||||
assert !(false && false);
|
|
||||||
|
|
||||||
assert true || true;
|
|
||||||
assert true || false;
|
|
||||||
assert false || true;
|
|
||||||
assert !(false || false);
|
|
|
@ -0,0 +1,68 @@
|
||||||
|
############
|
||||||
|
# BOOLEANS #
|
||||||
|
############
|
||||||
|
assert true;
|
||||||
|
|
||||||
|
assert !false;
|
||||||
|
assert !!true;
|
||||||
|
|
||||||
|
assert true && true;
|
||||||
|
assert !(true && false);
|
||||||
|
assert !(false && true);
|
||||||
|
assert !(false && false);
|
||||||
|
|
||||||
|
assert true || true;
|
||||||
|
assert true || false;
|
||||||
|
assert false || true;
|
||||||
|
assert !(false || false);
|
||||||
|
|
||||||
|
assert true == true;
|
||||||
|
assert false == false;
|
||||||
|
assert true != false;
|
||||||
|
assert false != true;
|
||||||
|
|
||||||
|
############
|
||||||
|
# INTEGERS #
|
||||||
|
############
|
||||||
|
assert 5 == 5;
|
||||||
|
assert 5 != 2;
|
||||||
|
|
||||||
|
assert 1 + 2 == 3;
|
||||||
|
assert 1 + -2 == -1;
|
||||||
|
assert 1 - 2 == -1;
|
||||||
|
assert 1 - -2 == 3;
|
||||||
|
assert 3 * 6 == 18;
|
||||||
|
assert 12 / 2 == 6;
|
||||||
|
assert 3 / 2 == 1;
|
||||||
|
assert 12 % 7 == 5;
|
||||||
|
assert 2 ** 3 == 8;
|
||||||
|
|
||||||
|
assert 1 + 2 * 3 == 7;
|
||||||
|
assert (1 + 2) * 3 == 9;
|
||||||
|
|
||||||
|
#########
|
||||||
|
# FLOAT #
|
||||||
|
#########
|
||||||
|
assert 2.3 == 2.3;
|
||||||
|
assert 2.3 != 2.4;
|
||||||
|
|
||||||
|
assert 1.0 + 2.0 == 3.0;
|
||||||
|
assert 1.0 + -2.0 == -1.0;
|
||||||
|
assert 1.0 - 2.0 == -1.0;
|
||||||
|
assert 1.0 - -2.0 == 3.0;
|
||||||
|
assert 3.0 * 6.0 == 18.0;
|
||||||
|
assert 12.0 / 2.0 == 6.0;
|
||||||
|
assert 3.0 / 2.0 == 1.5;
|
||||||
|
assert 12.0 % 7.0 == 5.0;
|
||||||
|
assert 2.0 ** 3.0 == 8.0;
|
||||||
|
|
||||||
|
###########
|
||||||
|
# STRINGS #
|
||||||
|
###########
|
||||||
|
assert "hello" == "hello";
|
||||||
|
assert "hello" != "world";
|
||||||
|
assert 3 * "a" == "aaa";
|
||||||
|
assert "a" * 3 == "aaa";
|
||||||
|
assert "a" + "b" == "ab";
|
||||||
|
assert 2 * "a" + "b" == "aab";
|
||||||
|
assert 2 * ("a" + 3 * "b") == "abbbabbb";
|
|
@ -0,0 +1,25 @@
|
||||||
|
assert 5 < 8 == true;
|
||||||
|
assert 5 < 5 == false;
|
||||||
|
assert 5 < 4 == false;
|
||||||
|
assert 5 <= 8 == true;
|
||||||
|
assert 5 <= 5 == true;
|
||||||
|
assert 5 <= 4 == false;
|
||||||
|
assert 14 > 8 == true;
|
||||||
|
assert 7 > 7 == false;
|
||||||
|
assert 14 > 32 == false;
|
||||||
|
assert 14 >= 8 == true;
|
||||||
|
assert 7 >= 7 == true;
|
||||||
|
assert 14 >= 32 == false;
|
||||||
|
|
||||||
|
assert 5.0 < 8.0 == true;
|
||||||
|
assert 5.0 < 5.0 == false;
|
||||||
|
assert 5.0 < 4.0 == false;
|
||||||
|
assert 5.0 <= 8.0 == true;
|
||||||
|
assert 5.0 <= 5.0 == true;
|
||||||
|
assert 5.0 <= 4.0 == false;
|
||||||
|
assert 14.0 > 8.0 == true;
|
||||||
|
assert 7.0 > 7.0 == false;
|
||||||
|
assert 14.0 > 32.0 == false;
|
||||||
|
assert 14.0 >= 8.0 == true;
|
||||||
|
assert 7.0 >= 7.0 == true;
|
||||||
|
assert 14.0 >= 32.0 == false;
|
191
vm/src/vm.c
191
vm/src/vm.c
|
@ -1,5 +1,8 @@
|
||||||
#include "vm.h"
|
#include "vm.h"
|
||||||
#include "commons.h"
|
#include "commons.h"
|
||||||
|
#include "program.h"
|
||||||
|
#include "type.h"
|
||||||
|
#include "value.h"
|
||||||
|
|
||||||
void vm_init(struct vm* self)
|
void vm_init(struct vm* self)
|
||||||
{
|
{
|
||||||
|
@ -134,13 +137,190 @@ int vm_exec(struct vm* self, struct program* program)
|
||||||
vm_push(self, program_push_constant(program, not));
|
vm_push(self, program_push_constant(program, not));
|
||||||
self->pc++;
|
self->pc++;
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
case OP_EQ: {
|
||||||
|
int rhs = vm_pop(self);
|
||||||
|
int lhs = vm_pop(self);
|
||||||
|
|
||||||
|
struct value* lhs_val = program->constant_pool.data[lhs];
|
||||||
|
struct value* rhs_val = program->constant_pool.data[rhs];
|
||||||
|
|
||||||
|
struct value* res = malloc(sizeof(struct value));
|
||||||
|
|
||||||
|
if (lhs_val->type.kind == TYPE_BOOL)
|
||||||
|
{
|
||||||
|
value_init_bool(res,
|
||||||
|
lhs_val->data.b == rhs_val->data.b,
|
||||||
|
lhs_val->line);
|
||||||
|
}
|
||||||
|
else if (lhs_val->type.kind == TYPE_INT)
|
||||||
|
{
|
||||||
|
value_init_bool(res,
|
||||||
|
lhs_val->data.i == rhs_val->data.i,
|
||||||
|
lhs_val->line);
|
||||||
|
}
|
||||||
|
else if (lhs_val->type.kind == TYPE_FLOAT)
|
||||||
|
{
|
||||||
|
value_init_bool(res,
|
||||||
|
lhs_val->data.f == rhs_val->data.f,
|
||||||
|
lhs_val->line);
|
||||||
|
}
|
||||||
|
else if (lhs_val->type.kind == TYPE_STRING)
|
||||||
|
{
|
||||||
|
value_init_bool(res,
|
||||||
|
strcmp(lhs_val->data.s, rhs_val->data.s) == 0,
|
||||||
|
lhs_val->line);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fprintf(stderr, "operation not supported\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
vm_push(self, program_push_constant(program, res));
|
||||||
|
self->pc++;
|
||||||
|
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case OP_ADD: {
|
||||||
|
int rhs = vm_pop(self);
|
||||||
|
int lhs = vm_pop(self);
|
||||||
|
|
||||||
|
struct value* lhs_val = program->constant_pool.data[lhs];
|
||||||
|
struct value* rhs_val = program->constant_pool.data[rhs];
|
||||||
|
|
||||||
|
struct value* res = value_try_new_add(lhs_val, rhs_val);
|
||||||
|
vm_push(self, program_push_constant(program, res));
|
||||||
|
|
||||||
|
self->pc++;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case OP_SUB: {
|
||||||
|
int rhs = vm_pop(self);
|
||||||
|
int lhs = vm_pop(self);
|
||||||
|
|
||||||
|
struct value* lhs_val = program->constant_pool.data[lhs];
|
||||||
|
struct value* rhs_val = program->constant_pool.data[rhs];
|
||||||
|
|
||||||
|
struct value* res = value_try_new_sub(lhs_val, rhs_val);
|
||||||
|
vm_push(self, program_push_constant(program, res));
|
||||||
|
|
||||||
|
self->pc++;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case OP_MUL: {
|
||||||
|
int rhs = vm_pop(self);
|
||||||
|
int lhs = vm_pop(self);
|
||||||
|
|
||||||
|
struct value* lhs_val = program->constant_pool.data[lhs];
|
||||||
|
struct value* rhs_val = program->constant_pool.data[rhs];
|
||||||
|
|
||||||
|
struct value* res = value_try_new_mul(lhs_val, rhs_val);
|
||||||
|
vm_push(self, program_push_constant(program, res));
|
||||||
|
|
||||||
|
self->pc++;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case OP_DIV: {
|
||||||
|
int rhs = vm_pop(self);
|
||||||
|
int lhs = vm_pop(self);
|
||||||
|
|
||||||
|
struct value* lhs_val = program->constant_pool.data[lhs];
|
||||||
|
struct value* rhs_val = program->constant_pool.data[rhs];
|
||||||
|
|
||||||
|
struct value* res = value_try_new_div(lhs_val, rhs_val);
|
||||||
|
vm_push(self, program_push_constant(program, res));
|
||||||
|
|
||||||
|
self->pc++;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case OP_MOD: {
|
||||||
|
int rhs = vm_pop(self);
|
||||||
|
int lhs = vm_pop(self);
|
||||||
|
|
||||||
|
struct value* lhs_val = program->constant_pool.data[lhs];
|
||||||
|
struct value* rhs_val = program->constant_pool.data[rhs];
|
||||||
|
|
||||||
|
struct value* res = value_try_new_mod(lhs_val, rhs_val);
|
||||||
|
vm_push(self, program_push_constant(program, res));
|
||||||
|
|
||||||
|
self->pc++;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case OP_POW: {
|
||||||
|
int rhs = vm_pop(self);
|
||||||
|
int lhs = vm_pop(self);
|
||||||
|
|
||||||
|
struct value* lhs_val = program->constant_pool.data[lhs];
|
||||||
|
struct value* rhs_val = program->constant_pool.data[rhs];
|
||||||
|
|
||||||
|
struct value* res = value_try_new_pow(lhs_val, rhs_val);
|
||||||
|
vm_push(self, program_push_constant(program, res));
|
||||||
|
|
||||||
|
self->pc++;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case OP_LT: {
|
||||||
|
int rhs = vm_pop(self);
|
||||||
|
int lhs = vm_pop(self);
|
||||||
|
|
||||||
|
struct value* lhs_val = program->constant_pool.data[lhs];
|
||||||
|
struct value* rhs_val = program->constant_pool.data[rhs];
|
||||||
|
|
||||||
|
struct value* res = value_try_new_lt(lhs_val, rhs_val);
|
||||||
|
vm_push(self, program_push_constant(program, res));
|
||||||
|
|
||||||
|
self->pc++;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case OP_LE: {
|
||||||
|
int rhs = vm_pop(self);
|
||||||
|
int lhs = vm_pop(self);
|
||||||
|
|
||||||
|
struct value* lhs_val = program->constant_pool.data[lhs];
|
||||||
|
struct value* rhs_val = program->constant_pool.data[rhs];
|
||||||
|
|
||||||
|
struct value* res = value_try_new_le(lhs_val, rhs_val);
|
||||||
|
vm_push(self, program_push_constant(program, res));
|
||||||
|
|
||||||
|
self->pc++;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case OP_GT: {
|
||||||
|
int rhs = vm_pop(self);
|
||||||
|
int lhs = vm_pop(self);
|
||||||
|
|
||||||
|
struct value* lhs_val = program->constant_pool.data[lhs];
|
||||||
|
struct value* rhs_val = program->constant_pool.data[rhs];
|
||||||
|
|
||||||
|
struct value* res = value_try_new_gt(lhs_val, rhs_val);
|
||||||
|
vm_push(self, program_push_constant(program, res));
|
||||||
|
|
||||||
|
self->pc++;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case OP_GE: {
|
||||||
|
int rhs = vm_pop(self);
|
||||||
|
int lhs = vm_pop(self);
|
||||||
|
|
||||||
|
struct value* lhs_val = program->constant_pool.data[lhs];
|
||||||
|
struct value* rhs_val = program->constant_pool.data[rhs];
|
||||||
|
|
||||||
|
struct value* res = value_try_new_ge(lhs_val, rhs_val);
|
||||||
|
vm_push(self, program_push_constant(program, res));
|
||||||
|
|
||||||
|
self->pc++;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
default: assert(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t vm_str(struct vm* self, char* buffer, size_t size)
|
size_t vm_str(struct vm* self, struct program* program,
|
||||||
|
char* buffer, size_t size)
|
||||||
{
|
{
|
||||||
assert(self);
|
assert(self);
|
||||||
assert(buffer);
|
assert(buffer);
|
||||||
|
@ -154,8 +334,13 @@ size_t vm_str(struct vm* self, char* buffer, size_t size)
|
||||||
|
|
||||||
for (size_t j=0; j<frame->sp; j++)
|
for (size_t j=0; j<frame->sp; j++)
|
||||||
{
|
{
|
||||||
sz += snprintf(buffer + sz, size - sz, "\t%zu: %d\n",
|
size_t k = frame->sp - 1 - j;
|
||||||
j, frame->stack[j]);
|
struct value* v = program->constant_pool.data[frame->stack[k]];
|
||||||
|
char val[GUX_STR_SIZE];
|
||||||
|
value_str(v, val, GUX_STR_SIZE);
|
||||||
|
|
||||||
|
sz += snprintf(buffer + sz, size - sz, "\t%zu: %d (%s)\n",
|
||||||
|
k, frame->stack[k], val);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,6 +30,7 @@ int vm_pop(struct vm* self);
|
||||||
|
|
||||||
int vm_exec(struct vm* self, struct program* program);
|
int vm_exec(struct vm* self, struct program* program);
|
||||||
|
|
||||||
size_t vm_str(struct vm* self, char* buffer, size_t size);
|
size_t vm_str(struct vm* self, struct program* program,
|
||||||
|
char* buffer, size_t size);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in New Issue