✨ var and const declaration, assignment and blocks.
parent
dd5c65b448
commit
75987d9780
|
@ -2,12 +2,15 @@
|
|||
#include "commons.h"
|
||||
#include "node.h"
|
||||
#include "opcodes.h"
|
||||
#include "syms.h"
|
||||
#include "value.h"
|
||||
#include "vec.h"
|
||||
|
||||
void compiler_init(struct compiler* self)
|
||||
void compiler_init(struct compiler* self, struct syms* syms)
|
||||
{
|
||||
assert(self);
|
||||
self->syms = syms;
|
||||
memset(self->error_msg, 0, GUX_STR_SIZE);
|
||||
}
|
||||
|
||||
void compiler_free(struct compiler* self)
|
||||
|
@ -25,6 +28,56 @@ int compiler_compile(struct compiler* self,
|
|||
|
||||
switch (node->type)
|
||||
{
|
||||
case NODE_ASSIGN: {
|
||||
struct node* ident = node->children.data[0];
|
||||
struct node* expr = node->children.data[1];
|
||||
struct syms_entry* entry = syms_try_get(self->syms, ident->value,
|
||||
node);
|
||||
assert(entry);
|
||||
|
||||
if (compiler_compile(self, expr, program) != 0)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
program_push_instr(program, OP_STORE, entry->addr);
|
||||
} break;
|
||||
|
||||
case NODE_IDENT: {
|
||||
struct syms_entry* entry = syms_try_get(self->syms,
|
||||
node->value,
|
||||
node);
|
||||
|
||||
if (!entry)
|
||||
{
|
||||
snprintf(self->error_msg, GUX_STR_SIZE,
|
||||
"%s is not defined", node->value);
|
||||
self->error_line = node->line;
|
||||
return 1;
|
||||
}
|
||||
|
||||
program_push_instr(program, OP_LOAD, entry->addr);
|
||||
} break;
|
||||
|
||||
case NODE_CONSTDECL:
|
||||
case NODE_VARDECL: {
|
||||
int err = compiler_compile(self, node->children.size == 1
|
||||
? node->children.data[0]
|
||||
: node->children.data[1],
|
||||
program);
|
||||
|
||||
if (err != 0)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct syms_entry* entry = syms_try_get(self->syms,
|
||||
node->value,
|
||||
node);
|
||||
assert(entry);
|
||||
|
||||
program_push_instr(program, OP_STORE, entry->addr);
|
||||
} break;
|
||||
case NODE_BOOL: {
|
||||
struct value* val = malloc(sizeof(struct value));
|
||||
value_init_bool(val,
|
||||
|
@ -56,7 +109,11 @@ int compiler_compile(struct compiler* self,
|
|||
} break;
|
||||
|
||||
case NODE_ASSERT: {
|
||||
compiler_compile(self, node->children.data[0], program);
|
||||
if (compiler_compile(self, node->children.data[0], program) != 0)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
size_t brt = program_push_instr(program, OP_BRT, NO_PARAM);
|
||||
program_push_instr(program, OP_PUSH, GUX_RET_ASSERT);
|
||||
size_t halt = program_push_instr(program, OP_HALT, node->line);
|
||||
|
@ -77,20 +134,34 @@ int compiler_compile(struct compiler* self,
|
|||
} break;
|
||||
|
||||
case NODE_EQ: {
|
||||
compiler_compile(self, node->children.data[0], program);
|
||||
compiler_compile(self, node->children.data[1], program);
|
||||
int err = compiler_compile(self, node->children.data[0], program);
|
||||
int err2 = compiler_compile(self, node->children.data[1], program);
|
||||
if (err != 0 || err2 != 0)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
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);
|
||||
int err = compiler_compile(self, node->children.data[0], program);
|
||||
int err2 = compiler_compile(self, node->children.data[1], program);
|
||||
|
||||
if (err != 0 || err2 != 0)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
program_push_instr(program, OP_EQ, NO_PARAM);
|
||||
program_push_instr(program, OP_NOT, NO_PARAM);
|
||||
} break;
|
||||
|
||||
case NODE_NOT: {
|
||||
compiler_compile(self, node->children.data[0], program);
|
||||
if (compiler_compile(self, node->children.data[0], program) != 0)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
program_push_instr(program, OP_NOT, NO_PARAM);
|
||||
} break;
|
||||
|
||||
|
@ -100,7 +171,10 @@ int compiler_compile(struct compiler* self,
|
|||
|
||||
for (size_t i=0; i<node->children.size; i++)
|
||||
{
|
||||
compiler_compile(self, node->children.data[i], program);
|
||||
if (compiler_compile(self, node->children.data[i], program) != 0)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
size_t* addr = malloc(sizeof(size_t));
|
||||
*addr = program_push_instr(program, OP_BRF, NO_PARAM); // to pivot
|
||||
|
@ -149,7 +223,10 @@ int compiler_compile(struct compiler* self,
|
|||
|
||||
for (size_t i=0; i<node->children.size; i++)
|
||||
{
|
||||
compiler_compile(self, node->children.data[i], program);
|
||||
if (compiler_compile(self, node->children.data[i], program) != 0)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
size_t* addr = malloc(sizeof(size_t));
|
||||
*addr = program_push_instr(program, OP_BRT, NO_PARAM); // to pivot
|
||||
|
@ -206,7 +283,10 @@ int compiler_compile(struct compiler* self,
|
|||
|
||||
for (size_t i=0; i<node->children.size; i++)
|
||||
{
|
||||
compiler_compile(self, node->children.data[i], program);
|
||||
if (compiler_compile(self, node->children.data[i], program) != 0)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
switch (node->type)
|
||||
|
@ -229,7 +309,10 @@ int compiler_compile(struct compiler* self,
|
|||
default: {
|
||||
for (size_t i=0; i<node->children.size; i++)
|
||||
{
|
||||
compiler_compile(self, node->children.data[i], program);
|
||||
if (compiler_compile(self, node->children.data[i], program) != 0)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
||||
|
|
|
@ -4,13 +4,15 @@
|
|||
#include <commons.h>
|
||||
#include <node.h>
|
||||
#include "program.h"
|
||||
#include "syms.h"
|
||||
|
||||
struct compiler {
|
||||
char* error_msg;
|
||||
char error_msg[GUX_STR_SIZE];
|
||||
struct syms* syms;
|
||||
int error_line;
|
||||
};
|
||||
|
||||
void compiler_init(struct compiler* self);
|
||||
void compiler_init(struct compiler* self, struct syms* syms);
|
||||
void compiler_free(struct compiler* self);
|
||||
|
||||
int compiler_compile(struct compiler* self,
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
#ifndef OPCODES_H
|
||||
#define OPCODES_H
|
||||
|
||||
#define OPCODES(G) \
|
||||
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_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),
|
||||
#define OPCODES(G) \
|
||||
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_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), G(OP_LOAD), G(OP_STORE)
|
||||
|
||||
#include <commons.h>
|
||||
|
||||
|
|
|
@ -67,10 +67,19 @@ size_t program_str(struct program* self, char* buffer, size_t size)
|
|||
struct instruction* instr = (struct instruction*)
|
||||
self->instructions.data[i];
|
||||
|
||||
sz += snprintf(buffer + sz, size - sz, "\t%ld: %s %d\n",
|
||||
i,
|
||||
OpcodesStr[instr->opcode] + strlen("OP_"),
|
||||
instr->param);
|
||||
if (instr->param != NO_PARAM)
|
||||
{
|
||||
sz += snprintf(buffer + sz, size - sz, "\t%ld: %s %d\n",
|
||||
i,
|
||||
OpcodesStr[instr->opcode] + strlen("OP_"),
|
||||
instr->param);
|
||||
}
|
||||
else
|
||||
{
|
||||
sz += snprintf(buffer + sz, size - sz, "\t%ld: %s\n",
|
||||
i,
|
||||
OpcodesStr[instr->opcode] + strlen("OP_"));
|
||||
}
|
||||
}
|
||||
|
||||
return sz;
|
||||
|
|
21
doc/gux.bnf
21
doc/gux.bnf
|
@ -1,11 +1,24 @@
|
|||
ROOT ::= EXPR*
|
||||
ROOT ::= INSTR*
|
||||
|
||||
INSTR ::=
|
||||
| EXPR semicolon
|
||||
| LEXPR semicolon
|
||||
| BEXPR
|
||||
|
||||
EXPR ::=
|
||||
EXPR ::= LEXPR | BEXPR
|
||||
|
||||
LEXPR ::=
|
||||
| OR
|
||||
| ASSERT EXPR
|
||||
| VARDECL
|
||||
| CONSTDECL
|
||||
| ASSIGN
|
||||
|
||||
BEXPR ::= BLOCK
|
||||
|
||||
BLOCK ::= obrace INSTR* cbrace
|
||||
VARDECL ::= var ident colon type? assign EXPR
|
||||
CONSTDECL ::= ident colon type? assign EXPR
|
||||
ASSIGN ::= ident assign EXPR
|
||||
|
||||
OR ::= AND (or AND)*
|
||||
AND ::= EQNE (and EQNE)*
|
||||
|
@ -16,5 +29,5 @@ FACTOR ::= POW ((mul|div|mod) POW)*
|
|||
POW ::= NOT (pow NOT)?
|
||||
|
||||
NOT ::= not* LITERAL
|
||||
LITERAL ::= BUILTIN | opar EXPR cpar
|
||||
LITERAL ::= ident | BUILTIN | opar EXPR cpar
|
||||
BUILTIN ::= bool | int | float | string
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include <type_checker.h>
|
||||
#include <compiler.h>
|
||||
#include <vm.h>
|
||||
#include <syms.h>
|
||||
|
||||
char* load_new_source(char const* path)
|
||||
{
|
||||
|
@ -49,6 +50,7 @@ int main(int argc, char** argv)
|
|||
static int show_ast = 0;
|
||||
static int show_bytecodes = 0;
|
||||
static int show_stack = 0;
|
||||
static int show_syms = 0;
|
||||
|
||||
int c;
|
||||
|
||||
|
@ -59,6 +61,7 @@ int main(int argc, char** argv)
|
|||
{"ast", no_argument, &show_ast, 1},
|
||||
{"bytecodes", no_argument, &show_bytecodes, 1},
|
||||
{"stack", no_argument, &show_stack, 1},
|
||||
{"symbols", no_argument, &show_syms, 1},
|
||||
{"help", no_argument, 0, 'h'}
|
||||
};
|
||||
|
||||
|
@ -156,8 +159,11 @@ int main(int argc, char** argv)
|
|||
printf("%s\n", msg);
|
||||
}
|
||||
|
||||
struct syms syms;
|
||||
syms_init(&syms);
|
||||
|
||||
struct type_checker checker;
|
||||
type_checker_init(&checker);
|
||||
type_checker_init(&checker, &syms);
|
||||
|
||||
if (type_checker_check(&checker, ast) != 0)
|
||||
{
|
||||
|
@ -166,6 +172,7 @@ int main(int argc, char** argv)
|
|||
checker.error_line,
|
||||
checker.error_msg);
|
||||
|
||||
syms_free(&syms);
|
||||
type_checker_free(&checker);
|
||||
|
||||
if (ast)
|
||||
|
@ -193,7 +200,7 @@ int main(int argc, char** argv)
|
|||
program_init(&program);
|
||||
|
||||
struct compiler compiler;
|
||||
compiler_init(&compiler);
|
||||
compiler_init(&compiler, &syms);
|
||||
|
||||
if (compiler_compile(&compiler, ast, &program) != 0)
|
||||
{
|
||||
|
@ -204,6 +211,7 @@ int main(int argc, char** argv)
|
|||
|
||||
compiler_free(&compiler);
|
||||
program_free(&program);
|
||||
syms_free(&syms);
|
||||
type_checker_free(&checker);
|
||||
|
||||
if (ast)
|
||||
|
@ -234,6 +242,13 @@ int main(int argc, char** argv)
|
|||
printf("%s\n", buffer);
|
||||
}
|
||||
|
||||
if (show_syms)
|
||||
{
|
||||
char buffer[GUX_STR_SIZE];
|
||||
syms_str(&syms, buffer, GUX_STR_SIZE);
|
||||
printf("%s\n", buffer);
|
||||
}
|
||||
|
||||
struct vm vm;
|
||||
vm_init(&vm);
|
||||
|
||||
|
@ -246,9 +261,11 @@ int main(int argc, char** argv)
|
|||
vm.error_line,
|
||||
vm.error_msg);
|
||||
|
||||
|
||||
vm_free(&vm);
|
||||
compiler_free(&compiler);
|
||||
program_free(&program);
|
||||
syms_free(&syms);
|
||||
type_checker_free(&checker);
|
||||
|
||||
if (ast)
|
||||
|
@ -284,6 +301,7 @@ int main(int argc, char** argv)
|
|||
vm_free(&vm);
|
||||
compiler_free(&compiler);
|
||||
program_free(&program);
|
||||
syms_free(&syms);
|
||||
type_checker_free(&checker);
|
||||
|
||||
if (ast)
|
||||
|
|
|
@ -12,6 +12,7 @@ add_library(gux-lang OBJECT
|
|||
src/value.c
|
||||
src/type_resolver.c
|
||||
src/type_checker.c
|
||||
src/syms.c
|
||||
)
|
||||
|
||||
set_property(TARGET gux-lang PROPERTY C_STANDARD 99)
|
||||
|
@ -41,6 +42,7 @@ add_executable(gux-lang-tests
|
|||
tests/type.c
|
||||
tests/value.c
|
||||
tests/type_checker.c
|
||||
tests/syms.c
|
||||
)
|
||||
|
||||
set_property(TARGET gux-lang-tests PROPERTY C_STANDARD 99)
|
||||
|
|
105
lang/src/lexer.c
105
lang/src/lexer.c
|
@ -12,27 +12,39 @@ void lexer_init(struct lexer* self, char const* source)
|
|||
|
||||
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, "var", NODE_VAR, "", 1);
|
||||
|
||||
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, "int", NODE_TYPE, "int", 1);
|
||||
lexer_add_tok(self, "float", NODE_TYPE, "float", 1);
|
||||
lexer_add_tok(self, "bool", NODE_TYPE, "bool", 1);
|
||||
lexer_add_tok(self, "string", NODE_TYPE, "string", 1);
|
||||
|
||||
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_OBRACE, "", 0);
|
||||
lexer_add_tok(self, "}", NODE_CBRACE, "", 0);
|
||||
lexer_add_tok(self, ":", NODE_COLON, "", 0);
|
||||
lexer_add_tok(self, "=", NODE_ASSIGN, "", 0);
|
||||
|
||||
lexer_add_tok(self, "||", NODE_OR, 0);
|
||||
lexer_add_tok(self, "!", NODE_NOT, 0);
|
||||
lexer_add_tok(self, "(", NODE_OPAR, 0);
|
||||
lexer_add_tok(self, ")", NODE_CPAR, 0);
|
||||
lexer_add_tok(self, ";", NODE_SEMICOLON, 0);
|
||||
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_OR, "", 0);
|
||||
lexer_add_tok(self, "!", NODE_NOT, "", 0);
|
||||
lexer_add_tok(self, "(", NODE_OPAR, "", 0);
|
||||
lexer_add_tok(self, ")", NODE_CPAR, "", 0);
|
||||
lexer_add_tok(self, ";", NODE_SEMICOLON, "", 0);
|
||||
}
|
||||
|
||||
void lexer_free(struct lexer* self)
|
||||
|
@ -111,12 +123,13 @@ struct node* lexer_next_new(struct lexer* self)
|
|||
{
|
||||
struct tok* tok = self->toks.data[i];
|
||||
|
||||
if (lexer_scan_text(self, tok->sym, &info))
|
||||
if ((tok->is_keyword && lexer_scan_keyword(self, tok->sym, &info))
|
||||
|| (!tok->is_keyword && lexer_scan_text(self, tok->sym, &info)))
|
||||
{
|
||||
if (best == NULL || info.position > pos)
|
||||
{
|
||||
struct node* node = malloc(sizeof(struct node));
|
||||
node_init(node, tok->type, "", self->line);
|
||||
node_init(node, tok->type, tok->value, self->line);
|
||||
|
||||
if (best)
|
||||
{
|
||||
|
@ -161,6 +174,14 @@ struct node* lexer_next_new(struct lexer* self)
|
|||
return node;
|
||||
}
|
||||
|
||||
if (lexer_scan_ident(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;
|
||||
}
|
||||
|
||||
|
@ -411,16 +432,60 @@ int lexer_scan_string(struct lexer* self,
|
|||
return 0;
|
||||
}
|
||||
|
||||
int lexer_scan_ident(struct lexer* self,
|
||||
struct token_info* info)
|
||||
{
|
||||
assert(self);
|
||||
assert(info);
|
||||
|
||||
char value[GUX_STR_SIZE];
|
||||
size_t size = 0;
|
||||
size_t cursor = self->cursor;
|
||||
|
||||
while (cursor < strlen(self->source))
|
||||
{
|
||||
char c = self->source[cursor];
|
||||
|
||||
if (isalpha(c)
|
||||
|| c == '_'
|
||||
|| c == '!'
|
||||
|| c == '?'
|
||||
|| (size > 0 && isdigit(c)))
|
||||
{
|
||||
value[size] = c;
|
||||
size++;
|
||||
cursor++;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (size > 0)
|
||||
{
|
||||
info->position = cursor;
|
||||
memcpy(info->value, value, size);
|
||||
info->value[size] = '\0';
|
||||
info->type = NODE_IDENT;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void lexer_add_tok(struct lexer* self,
|
||||
char* sym,
|
||||
enum NodeType type,
|
||||
char* value,
|
||||
int is_keyword)
|
||||
{
|
||||
(void) self;
|
||||
struct tok* tok = malloc(sizeof(struct tok));
|
||||
tok->sym = sym;
|
||||
tok->type = type;
|
||||
memcpy(tok->value, value, GUX_STR_SIZE);
|
||||
tok->is_keyword = is_keyword;
|
||||
|
||||
vec_push(&self->toks, tok);
|
||||
|
|
|
@ -9,6 +9,7 @@ struct tok {
|
|||
char* sym;
|
||||
enum NodeType type;
|
||||
int is_keyword;
|
||||
char value[GUX_STR_SIZE];
|
||||
};
|
||||
|
||||
struct lexer {
|
||||
|
@ -52,9 +53,13 @@ int lexer_scan_float(struct lexer* self,
|
|||
int lexer_scan_string(struct lexer* self,
|
||||
struct token_info* info);
|
||||
|
||||
int lexer_scan_ident(struct lexer* self,
|
||||
struct token_info* info);
|
||||
|
||||
void lexer_add_tok(struct lexer* self,
|
||||
char* sym,
|
||||
enum NodeType type,
|
||||
char* value,
|
||||
int is_keyword);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -11,6 +11,7 @@ void node_init(struct node* self, enum NodeType type,
|
|||
self->value = strdup(value);
|
||||
vec_init(&self->children, 1);
|
||||
self->line = line;
|
||||
self->parent = NULL;
|
||||
}
|
||||
|
||||
void node_free(struct node* self)
|
||||
|
@ -43,6 +44,7 @@ struct node* node_add_child(struct node* self, struct node* child)
|
|||
{
|
||||
assert(self);
|
||||
vec_push(&self->children, child);
|
||||
child->parent = self;
|
||||
return child;
|
||||
}
|
||||
|
||||
|
@ -81,3 +83,76 @@ size_t node_str(struct node* self, char* buffer, size_t size)
|
|||
|
||||
return sz;
|
||||
}
|
||||
|
||||
int node_depth(struct node* self)
|
||||
{
|
||||
assert(self);
|
||||
struct node* itr = self;
|
||||
int depth = 0;
|
||||
|
||||
while (itr != NULL)
|
||||
{
|
||||
if (itr->type == NODE_BLOCK)
|
||||
{
|
||||
depth++;
|
||||
}
|
||||
|
||||
itr = itr->parent;
|
||||
}
|
||||
|
||||
return depth;
|
||||
}
|
||||
|
||||
struct node* node_try_find_parent(struct node* self, enum NodeType type)
|
||||
{
|
||||
assert(self);
|
||||
|
||||
struct node* p = self;
|
||||
|
||||
while (p != NULL && p->type != type)
|
||||
{
|
||||
p = p->parent;
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
size_t node_parent_index(struct node* self)
|
||||
{
|
||||
assert(self);
|
||||
|
||||
struct node* parent = self->parent;
|
||||
|
||||
if (parent == NULL)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (size_t i=0; i<parent->children.size; i++)
|
||||
{
|
||||
if (parent->children.data[i] == self)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t node_block_index(struct node* self)
|
||||
{
|
||||
assert(self);
|
||||
struct node* block = node_try_find_parent(self, NODE_BLOCK);
|
||||
|
||||
if (block == NULL)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (self->parent == block)
|
||||
{
|
||||
return node_parent_index(self);
|
||||
}
|
||||
|
||||
return node_block_index(self->parent);
|
||||
}
|
||||
|
|
|
@ -11,14 +11,17 @@
|
|||
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)
|
||||
G(NODE_MOD), G(NODE_POW), G(NODE_LT), G(NODE_LE), G(NODE_GT), \
|
||||
G(NODE_GE), G(NODE_COLON), G(NODE_ASSIGN), G(NODE_IDENT), \
|
||||
G(NODE_TYPE), G(NODE_VARDECL), G(NODE_CONSTDECL), G(NODE_VAR), \
|
||||
G(NODE_OBRACE), G(NODE_CBRACE), G(NODE_BLOCK)
|
||||
|
||||
GUX_ENUM_H(NodeType, NODE_TYPE);
|
||||
|
||||
struct node {
|
||||
enum NodeType type;
|
||||
char* value;
|
||||
struct node* parent;
|
||||
struct vec children;
|
||||
int line;
|
||||
};
|
||||
|
@ -34,4 +37,10 @@ struct node* node_add_child(struct node* self, struct node* child);
|
|||
|
||||
size_t node_str(struct node* self, char* buffer, size_t size);
|
||||
|
||||
int node_depth(struct node* self);
|
||||
struct node* node_try_find_parent(struct node* self, enum NodeType type);
|
||||
size_t node_parent_index(struct node* self);
|
||||
|
||||
size_t node_block_index(struct node* self);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -127,6 +127,14 @@ int parser_try_consume(struct parser* self, enum NodeType type)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int parser_start_bexpr(struct parser* self)
|
||||
{
|
||||
assert(self);
|
||||
struct node* tok = self->tokens.data[self->cursor];
|
||||
|
||||
return tok->type == NODE_OBRACE;
|
||||
}
|
||||
|
||||
struct node* parser_try_new_root(struct parser* self, char const* source)
|
||||
{
|
||||
assert(self);
|
||||
|
@ -163,22 +171,42 @@ struct node* parser_try_new_root(struct parser* self, char const* source)
|
|||
struct node* parser_try_new_instr(struct parser* self)
|
||||
{
|
||||
assert(self);
|
||||
struct node* node = parser_try_new_expr(self);
|
||||
|
||||
if (parser_ensure(self, NODE_SEMICOLON) != 0)
|
||||
if (parser_start_bexpr(self))
|
||||
{
|
||||
node_free(node);
|
||||
free(node);
|
||||
return NULL;
|
||||
return parser_try_new_bexpr(self);
|
||||
}
|
||||
else
|
||||
{
|
||||
struct node* node = parser_try_new_lexpr(self);
|
||||
|
||||
return node;
|
||||
if (parser_ensure(self, NODE_SEMICOLON) != 0)
|
||||
{
|
||||
node_free(node);
|
||||
free(node);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
}
|
||||
|
||||
struct node* parser_try_new_expr(struct parser* self)
|
||||
{
|
||||
assert(self);
|
||||
|
||||
if (parser_start_bexpr(self))
|
||||
{
|
||||
return parser_try_new_bexpr(self);
|
||||
}
|
||||
|
||||
return parser_try_new_lexpr(self);
|
||||
}
|
||||
|
||||
struct node* parser_try_new_lexpr(struct parser* self)
|
||||
{
|
||||
assert(self);
|
||||
|
||||
if (parser_type_is(self, NODE_ASSERT, 0))
|
||||
{
|
||||
struct node* node = malloc(sizeof(struct node));
|
||||
|
@ -198,6 +226,20 @@ struct node* parser_try_new_expr(struct parser* self)
|
|||
|
||||
return node;
|
||||
}
|
||||
else if (parser_type_is(self, NODE_VAR, 0))
|
||||
{
|
||||
return parser_try_new_vardecl(self);
|
||||
}
|
||||
else if (parser_type_is(self, NODE_IDENT, 0)
|
||||
&& parser_type_is(self, NODE_COLON, 1))
|
||||
{
|
||||
return parser_try_new_constdecl(self);
|
||||
}
|
||||
else if (parser_type_is(self, NODE_IDENT, 0)
|
||||
&& parser_type_is(self, NODE_ASSIGN, 1))
|
||||
{
|
||||
return parser_try_new_assign(self);
|
||||
}
|
||||
|
||||
struct node* node = parser_try_new_or(self);
|
||||
|
||||
|
@ -209,6 +251,129 @@ struct node* parser_try_new_expr(struct parser* self)
|
|||
return node;
|
||||
}
|
||||
|
||||
struct node* parser_try_new_bexpr(struct parser* self)
|
||||
{
|
||||
assert(self);
|
||||
return parser_try_new_block(self);
|
||||
}
|
||||
|
||||
struct node* parser_try_new_block(struct parser* self)
|
||||
{
|
||||
|
||||
parser_try_consume(self, NODE_OBRACE);
|
||||
struct node* node = malloc(sizeof(struct node));
|
||||
node_init(node, NODE_BLOCK, "", parser_current_line(self));
|
||||
|
||||
while (!parser_type_is(self, NODE_CBRACE, 0))
|
||||
{
|
||||
node_add_child(node, parser_try_new_instr(self));
|
||||
}
|
||||
|
||||
if (parser_try_consume(self, NODE_CBRACE) != 0)
|
||||
{
|
||||
node_free(node);
|
||||
free(node);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
struct node* parser_try_new_assign(struct parser* self)
|
||||
{
|
||||
assert(self);
|
||||
struct node* node = malloc(sizeof(struct node));
|
||||
node_init(node, NODE_ASSIGN, "", parser_current_line(self));
|
||||
|
||||
node_add_child(node, parser_try_consume_new(self, NODE_IDENT));
|
||||
self->cursor++; // assign
|
||||
node_add_child(node, parser_try_new_expr(self));
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
struct node* parser_try_new_vardecl(struct parser* self)
|
||||
{
|
||||
assert(self);
|
||||
self->cursor++; // var
|
||||
struct node* current = self->tokens.data[self->cursor];
|
||||
|
||||
struct node* node = malloc(sizeof(struct node));
|
||||
node_init(node, NODE_VARDECL, current->value, parser_current_line(self));
|
||||
|
||||
self->cursor++; // ident
|
||||
|
||||
if (parser_try_consume(self, NODE_COLON) != 0)
|
||||
{
|
||||
node_free(node);
|
||||
free(node);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (parser_type_is(self, NODE_TYPE, 0))
|
||||
{
|
||||
current = self->tokens.data[self->cursor];
|
||||
struct node* ty = malloc(sizeof(struct node));
|
||||
node_init(ty, NODE_TYPE, current->value, parser_current_line(self));
|
||||
|
||||
node_add_child(node, ty);
|
||||
self->cursor++;
|
||||
}
|
||||
|
||||
if (parser_try_consume(self, NODE_ASSIGN) != 0)
|
||||
{
|
||||
node_free(node);
|
||||
free(node);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
node_add_child(node, parser_try_new_expr(self));
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
struct node* parser_try_new_constdecl(struct parser* self)
|
||||
{
|
||||
assert(self);
|
||||
struct node* current = self->tokens.data[self->cursor];
|
||||
|
||||
struct node* node = malloc(sizeof(struct node));
|
||||
node_init(node, NODE_CONSTDECL, current->value, parser_current_line(self));
|
||||
|
||||
self->cursor++; // ident
|
||||
|
||||
if (parser_try_consume(self, NODE_COLON) != 0)
|
||||
{
|
||||
node_free(node);
|
||||
free(node);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (parser_type_is(self, NODE_TYPE, 0))
|
||||
{
|
||||
current = self->tokens.data[self->cursor];
|
||||
struct node* ty = malloc(sizeof(struct node));
|
||||
node_init(ty, NODE_TYPE, current->value, parser_current_line(self));
|
||||
|
||||
node_add_child(node, ty);
|
||||
self->cursor++;
|
||||
}
|
||||
|
||||
if (parser_try_consume(self, NODE_ASSIGN) != 0)
|
||||
{
|
||||
node_free(node);
|
||||
free(node);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct node* expr = parser_try_new_expr(self);
|
||||
assert(expr);
|
||||
node_add_child(node, expr);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
struct node* parser_try_new_or(struct parser* self)
|
||||
{
|
||||
struct node* lhs = parser_try_new_and(self);
|
||||
|
@ -388,6 +553,11 @@ struct node* parser_try_new_literal(struct parser* self)
|
|||
return node;
|
||||
}
|
||||
|
||||
if (parser_type_is(self, NODE_IDENT, 0))
|
||||
{
|
||||
return parser_try_consume_new(self, NODE_IDENT);
|
||||
}
|
||||
|
||||
return parser_try_new_builtin(self);
|
||||
}
|
||||
|
||||
|
|
|
@ -24,9 +24,17 @@ struct node* parser_try_consume_new(struct parser* self, enum NodeType type);
|
|||
int parser_try_consume(struct parser* self, enum NodeType type);
|
||||
int parser_ensure(struct parser* self, enum NodeType type);
|
||||
|
||||
int parser_start_bexpr(struct parser* self);
|
||||
|
||||
struct node* parser_try_new_root(struct parser* self, char const* source);
|
||||
struct node* parser_try_new_instr(struct parser* self);
|
||||
struct node* parser_try_new_expr(struct parser* self);
|
||||
struct node* parser_try_new_lexpr(struct parser* self);
|
||||
struct node* parser_try_new_bexpr(struct parser* self);
|
||||
struct node* parser_try_new_block(struct parser* self);
|
||||
struct node* parser_try_new_assign(struct parser* self);
|
||||
struct node* parser_try_new_vardecl(struct parser* self);
|
||||
struct node* parser_try_new_constdecl(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_eqne(struct parser* self);
|
||||
|
|
|
@ -0,0 +1,191 @@
|
|||
#include "syms.h"
|
||||
#include "commons.h"
|
||||
#include "type.h"
|
||||
#include "node.h"
|
||||
|
||||
void syms_init(struct syms* self)
|
||||
{
|
||||
assert(self);
|
||||
vec_init(&self->types, 1);
|
||||
vec_init(&self->entries, 1);
|
||||
self->addr_counter = 0;
|
||||
size_t k = 0;
|
||||
syms_add_type(self, k++, TYPE_INT, 0);
|
||||
syms_add_type(self, k++, TYPE_FLOAT, 0);
|
||||
syms_add_type(self, k++, TYPE_BOOL, 0);
|
||||
syms_add_type(self, k++, TYPE_STRING, 0);
|
||||
}
|
||||
|
||||
void syms_free(struct syms* self)
|
||||
{
|
||||
assert(self);
|
||||
|
||||
for (size_t i=0; i<self->types.size; i++)
|
||||
{
|
||||
struct syms_type* st = self->types.data[i];
|
||||
type_free(&st->type);
|
||||
}
|
||||
|
||||
vec_free_elements(&self->types);
|
||||
vec_free(&self->types);
|
||||
|
||||
vec_free_elements(&self->entries);
|
||||
vec_free(&self->entries);
|
||||
}
|
||||
|
||||
void syms_add_type(struct syms* self, size_t id, enum TypeKind type,
|
||||
int count, ...)
|
||||
{
|
||||
va_list va;
|
||||
va_start(va, count);
|
||||
|
||||
struct syms_type* ty = malloc(sizeof(struct syms_type));
|
||||
ty->id = id;
|
||||
type_init(&ty->type, type);
|
||||
|
||||
for (int i=0; i<count; i++)
|
||||
{
|
||||
type_add_subtype(&ty->type, va_arg(va, enum TypeKind));
|
||||
}
|
||||
|
||||
vec_push(&self->types, ty);
|
||||
va_end(va);
|
||||
}
|
||||
|
||||
size_t syms_type_id(struct syms* self, enum TypeKind type)
|
||||
{
|
||||
assert(self);
|
||||
|
||||
for (size_t i=0; i<self->types.size; i++)
|
||||
{
|
||||
struct syms_type* ty = self->types.data[i];
|
||||
|
||||
if (ty->type.kind == type)
|
||||
{
|
||||
return ty->id;
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(stderr, "type not found\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
struct type* syms_try_type(struct syms* self, size_t id)
|
||||
{
|
||||
for (size_t i=0; i<self->types.size; i++)
|
||||
{
|
||||
struct syms_type* ty = self->types.data[i];
|
||||
|
||||
if (ty->id == id)
|
||||
{
|
||||
return &ty->type;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void syms_declare(struct syms* self, char* name,
|
||||
size_t type, int is_var,
|
||||
struct node* node)
|
||||
{
|
||||
assert(self);
|
||||
struct syms_entry* entry = malloc(sizeof(struct syms_entry));
|
||||
size_t sz = strlen(name);
|
||||
|
||||
if (sz > GUX_STR_SIZE)
|
||||
{
|
||||
sz = GUX_STR_SIZE;
|
||||
}
|
||||
|
||||
memset(entry->name, 0, GUX_STR_SIZE);
|
||||
memcpy(entry->name, name, sz);
|
||||
|
||||
entry->type = type;
|
||||
entry->addr = self->addr_counter++;
|
||||
entry->is_var = is_var;
|
||||
entry->node = node;
|
||||
|
||||
entry->depth = 0;
|
||||
struct node* itr = entry->node;
|
||||
|
||||
while (itr != NULL)
|
||||
{
|
||||
if (itr->type == NODE_BLOCK)
|
||||
{
|
||||
entry->depth++;
|
||||
}
|
||||
|
||||
itr = itr->parent;
|
||||
}
|
||||
|
||||
vec_push(&self->entries, entry);
|
||||
}
|
||||
|
||||
struct syms_entry* syms_try_get(struct syms* self, char* name,
|
||||
struct node* node)
|
||||
{
|
||||
assert(self);
|
||||
|
||||
struct syms_entry* res = NULL;
|
||||
int max_depth = -1;
|
||||
int depth = node ? node_depth(node) : 0;
|
||||
|
||||
for (size_t i=0; i<self->entries.size; i++)
|
||||
{
|
||||
struct syms_entry* entry = self->entries.data[i];
|
||||
|
||||
if (strcmp(entry->name, name) == 0 && entry->depth > max_depth
|
||||
&& entry->depth <= depth)
|
||||
{
|
||||
if (node && entry->node && entry->depth == depth)
|
||||
{
|
||||
struct node* node_block =
|
||||
node_try_find_parent(node, NODE_BLOCK);
|
||||
struct node* entry_block =
|
||||
node_try_find_parent(entry->node, NODE_BLOCK);
|
||||
|
||||
if (node_block == entry_block)
|
||||
{
|
||||
int node_index = node_block_index(node);
|
||||
int entry_index = node_block_index(entry->node);
|
||||
|
||||
if (entry_index > node_index)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
max_depth = entry->depth;
|
||||
res = entry;
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
size_t syms_str(struct syms* self, char* buffer, size_t size)
|
||||
{
|
||||
assert(self);
|
||||
assert(buffer);
|
||||
|
||||
size_t sz = 0;
|
||||
|
||||
for (size_t i=0; i<self->entries.size; i++)
|
||||
{
|
||||
struct syms_entry* entry = self->entries.data[i];
|
||||
struct type* type = syms_try_type(self, entry->type);
|
||||
sz += snprintf(buffer + sz, size - sz, "[%zu] ", entry->addr);
|
||||
sz += type_str(type, buffer + sz, size - sz);
|
||||
sz += snprintf(buffer + sz, size - sz, " %s, ", entry->name);
|
||||
sz += snprintf(buffer + sz, size - sz, " depth=%d, ", entry->depth);
|
||||
sz += snprintf(buffer + sz, size - sz, " block=%p\n", entry->node);
|
||||
}
|
||||
|
||||
return sz;
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
#ifndef SYMS_H
|
||||
#define SYMS_H
|
||||
|
||||
#include <commons.h>
|
||||
#include <vec.h>
|
||||
#include <type.h>
|
||||
|
||||
#define SYM_IS_VAR 1
|
||||
#define SYM_IS_CONST 0
|
||||
|
||||
struct syms_type {
|
||||
size_t id;
|
||||
struct type type;
|
||||
};
|
||||
|
||||
struct syms_entry {
|
||||
char name[GUX_STR_SIZE];
|
||||
size_t type;
|
||||
size_t addr;
|
||||
int is_var;
|
||||
int depth;
|
||||
struct node* node;
|
||||
};
|
||||
|
||||
struct syms {
|
||||
struct vec types;
|
||||
struct vec entries;
|
||||
size_t addr_counter;
|
||||
};
|
||||
|
||||
void syms_init(struct syms* self);
|
||||
void syms_free(struct syms* self);
|
||||
|
||||
void syms_add_type(struct syms* self, size_t id, enum TypeKind type,
|
||||
int count, ...);
|
||||
|
||||
size_t syms_type_id(struct syms* self, enum TypeKind type);
|
||||
struct type* syms_try_type(struct syms* self, size_t id);
|
||||
|
||||
void syms_declare(struct syms* self, char* name,
|
||||
size_t type, int is_var,
|
||||
struct node* node);
|
||||
|
||||
struct syms_entry* syms_try_get(struct syms* self, char* name,
|
||||
struct node* node);
|
||||
|
||||
size_t syms_str(struct syms* self, char* buffer, size_t size);
|
||||
|
||||
#endif
|
|
@ -3,11 +3,13 @@
|
|||
#include "node.h"
|
||||
#include "type.h"
|
||||
#include "type_resolver.h"
|
||||
#include "syms.h"
|
||||
|
||||
void type_checker_init(struct type_checker* self)
|
||||
void type_checker_init(struct type_checker* self, struct syms* syms)
|
||||
{
|
||||
assert(self);
|
||||
type_resolver_init(&self->resolver);
|
||||
self->syms = syms;
|
||||
type_resolver_init(&self->resolver, self->syms);
|
||||
memset(self->error_msg, 0, GUX_STR_SIZE);
|
||||
}
|
||||
|
||||
|
@ -25,6 +27,161 @@ int type_checker_check(struct type_checker* self,
|
|||
|
||||
switch (node->type)
|
||||
{
|
||||
case NODE_ASSIGN: {
|
||||
struct node* ident = node->children.data[0];
|
||||
struct node* expr = node->children.data[1];
|
||||
|
||||
struct syms_entry* entry = syms_try_get(self->syms, ident->value,
|
||||
node);
|
||||
|
||||
if (!entry)
|
||||
{
|
||||
snprintf(self->error_msg, GUX_STR_SIZE, "<%s> is not declared",
|
||||
ident->value);
|
||||
self->error_line = ident->line;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!entry->is_var)
|
||||
{
|
||||
snprintf(self->error_msg, GUX_STR_SIZE, "<%s> is not mutable",
|
||||
ident->value);
|
||||
self->error_line = ident->line;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct type* var_type = syms_try_type(self->syms, entry->type);
|
||||
|
||||
struct type expr_type;
|
||||
type_init(&expr_type, TYPE_VOID);
|
||||
|
||||
type_resolver_resolve(&self->resolver, expr, &expr_type);
|
||||
|
||||
if (!type_equals(var_type, &expr_type))
|
||||
{
|
||||
type_checker_error_msg(self, node, var_type, &expr_type);
|
||||
type_free(&expr_type);
|
||||
return 1;
|
||||
}
|
||||
|
||||
type_free(&expr_type);
|
||||
} return 0;
|
||||
|
||||
case NODE_VARDECL:
|
||||
case NODE_CONSTDECL:{
|
||||
struct syms_entry* entry = syms_try_get(self->syms,
|
||||
node->value,
|
||||
node);
|
||||
|
||||
if (entry && entry->depth == node_depth(node))
|
||||
{
|
||||
snprintf(self->error_msg, GUX_STR_SIZE,
|
||||
"%s is already defined", node->value);
|
||||
self->error_line = node->line;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
char* type_name = NULL;
|
||||
struct node* expr = NULL;
|
||||
int is_var = node->type == NODE_VARDECL;
|
||||
|
||||
for (size_t i=0; i<node->children.size; i++)
|
||||
{
|
||||
type_checker_check(self, node->children.data[i]);
|
||||
}
|
||||
|
||||
if (node->children.size == 1)
|
||||
{
|
||||
expr = node->children.data[0];
|
||||
|
||||
struct type ty;
|
||||
type_init(&ty, TYPE_VOID);
|
||||
if (type_resolver_resolve(&self->resolver, expr, &ty) != 0)
|
||||
{
|
||||
type_free(&ty);
|
||||
return 1;
|
||||
}
|
||||
|
||||
syms_declare(self->syms, node->value,
|
||||
syms_type_id(self->syms, ty.kind), is_var,
|
||||
node);
|
||||
|
||||
type_free(&ty);
|
||||
}
|
||||
else if (node->children.size == 2)
|
||||
{
|
||||
expr = node->children.data[1];
|
||||
|
||||
type_name = ((struct node*) node->children.data[0])->value;
|
||||
struct type ty;
|
||||
|
||||
if (strcmp(type_name, "int") == 0)
|
||||
{
|
||||
type_init(&ty, TYPE_INT);
|
||||
}
|
||||
else if (strcmp(type_name, "float") == 0)
|
||||
{
|
||||
type_init(&ty, TYPE_FLOAT);
|
||||
}
|
||||
else if (strcmp(type_name, "bool") == 0)
|
||||
{
|
||||
type_init(&ty, TYPE_BOOL);
|
||||
}
|
||||
else if (strcmp(type_name, "string") == 0)
|
||||
{
|
||||
type_init(&ty, TYPE_STRING);
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf(self->error_msg, GUX_STR_SIZE,
|
||||
"unknown type <%s>",
|
||||
type_name);
|
||||
|
||||
self->error_line = node->line;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct type expr_ty;
|
||||
type_init(&expr_ty, TYPE_VOID);
|
||||
if (type_resolver_resolve(&self->resolver, expr, &expr_ty) != 0)
|
||||
{
|
||||
type_free(&expr_ty);
|
||||
type_free(&ty);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!type_equals(&ty, &expr_ty))
|
||||
{
|
||||
char ty0[GUX_STR_SIZE / 3];
|
||||
type_str(&expr_ty, ty0, GUX_STR_SIZE/3);
|
||||
|
||||
char ty1[GUX_STR_SIZE / 3];
|
||||
type_str(&ty, ty1, GUX_STR_SIZE/3);
|
||||
|
||||
snprintf(self->error_msg, GUX_STR_SIZE,
|
||||
"mismatch type: expected <%s>, got <%s>",
|
||||
ty1,
|
||||
ty0);
|
||||
|
||||
self->error_line = node->line;
|
||||
|
||||
type_free(&ty);
|
||||
type_free(&expr_ty);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
syms_declare(self->syms, node->value,
|
||||
syms_type_id(self->syms, ty.kind), is_var, node);
|
||||
|
||||
type_free(&ty);
|
||||
type_free(&expr_ty);
|
||||
}
|
||||
} return 0;
|
||||
case NODE_LT:
|
||||
case NODE_LE:
|
||||
case NODE_GT:
|
||||
|
@ -163,6 +320,18 @@ int type_checker_check(struct type_checker* self,
|
|||
|
||||
if (type_resolver_resolve(&self->resolver, node, &type) != 0)
|
||||
{
|
||||
struct type lhs;
|
||||
type_init(&lhs, TYPE_VOID);
|
||||
type_resolver_resolve(&self->resolver, node->children.data[0], &lhs);
|
||||
|
||||
struct type rhs;
|
||||
type_init(&rhs, TYPE_VOID);
|
||||
type_resolver_resolve(&self->resolver, node->children.data[1], &rhs);
|
||||
|
||||
type_checker_error_msg(self, node, &lhs, &rhs);
|
||||
|
||||
type_free(&rhs);
|
||||
type_free(&lhs);
|
||||
type_free(&type);
|
||||
return 1;
|
||||
}
|
||||
|
@ -211,3 +380,29 @@ int type_checker_ensure_kind(struct type_checker* self,
|
|||
type_free(&type);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void type_checker_error_msg(struct type_checker* self,
|
||||
struct node* node,
|
||||
struct type* want,
|
||||
struct type* have)
|
||||
{
|
||||
assert(self);
|
||||
assert(node);
|
||||
assert(want);
|
||||
assert(have);
|
||||
|
||||
size_t const sz = GUX_STR_SIZE / 3;
|
||||
|
||||
char w[sz];
|
||||
type_str(want, w, sz);
|
||||
|
||||
char h[sz];
|
||||
type_str(have, h, sz);
|
||||
|
||||
snprintf(self->error_msg, GUX_STR_SIZE,
|
||||
"type mismatch: expected <%s>, got <%s>",
|
||||
w,
|
||||
h);
|
||||
|
||||
self->error_line = node->line;
|
||||
}
|
||||
|
|
|
@ -7,12 +7,13 @@
|
|||
#include <type_resolver.h>
|
||||
|
||||
struct type_checker {
|
||||
struct syms* syms;
|
||||
struct type_resolver resolver;
|
||||
char error_msg[GUX_STR_SIZE];
|
||||
int error_line;
|
||||
};
|
||||
|
||||
void type_checker_init(struct type_checker* self);
|
||||
void type_checker_init(struct type_checker* self, struct syms* syms);
|
||||
void type_checker_free(struct type_checker* self);
|
||||
|
||||
int type_checker_check(struct type_checker* self,
|
||||
|
@ -22,4 +23,9 @@ int type_checker_ensure_kind(struct type_checker* self,
|
|||
struct node* node,
|
||||
enum TypeKind kind);
|
||||
|
||||
void type_checker_error_msg(struct type_checker* self,
|
||||
struct node* node,
|
||||
struct type* want,
|
||||
struct type* have);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
#include "type_resolver.h"
|
||||
#include "node.h"
|
||||
#include "syms.h"
|
||||
#include "type.h"
|
||||
|
||||
void type_resolver_init(struct type_resolver* self)
|
||||
void type_resolver_init(struct type_resolver* self, struct syms* syms)
|
||||
{
|
||||
assert(self);
|
||||
self->syms = syms;
|
||||
}
|
||||
|
||||
void type_resolver_free(struct type_resolver* self)
|
||||
|
@ -24,6 +26,45 @@ int type_resolver_resolve(struct type_resolver* self,
|
|||
|
||||
switch (node->type)
|
||||
{
|
||||
|
||||
case NODE_ASSIGN: {
|
||||
return type_resolver_resolve(self, node->children.data[1], type);
|
||||
} return 0;
|
||||
|
||||
|
||||
case NODE_BLOCK: {
|
||||
size_t last = node->children.size - 1;
|
||||
return type_resolver_resolve(self, node->children.data[last], type);
|
||||
} return 0;
|
||||
|
||||
case NODE_VARDECL:
|
||||
case NODE_CONSTDECL: {
|
||||
if (node->children.size == 1)
|
||||
{
|
||||
type_resolver_resolve(self, node->children.data[0], type);
|
||||
}
|
||||
else
|
||||
{
|
||||
type_resolver_resolve(self, node->children.data[1], type);
|
||||
}
|
||||
|
||||
} return 0;
|
||||
|
||||
case NODE_IDENT: {
|
||||
struct syms_entry* entry = syms_try_get(self->syms,
|
||||
node->value,
|
||||
node_depth(node));
|
||||
|
||||
if (!entry)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct type* ty = syms_try_type(self->syms, entry->type);
|
||||
|
||||
type->kind = ty->kind;
|
||||
} return 0;
|
||||
|
||||
case NODE_EQ:
|
||||
case NODE_NE:
|
||||
case NODE_ASSERT:
|
||||
|
|
|
@ -6,10 +6,10 @@
|
|||
#include <type.h>
|
||||
|
||||
struct type_resolver {
|
||||
|
||||
struct syms* syms;
|
||||
};
|
||||
|
||||
void type_resolver_init(struct type_resolver* self);
|
||||
void type_resolver_init(struct type_resolver* self, struct syms* syms);
|
||||
void type_resolver_free(struct type_resolver* self);
|
||||
|
||||
int type_resolver_resolve(struct type_resolver* self,
|
||||
|
|
|
@ -105,5 +105,23 @@ Test(lexer, comparisons) {
|
|||
"GE",
|
||||
"LT",
|
||||
"LE");
|
||||
|
||||
}
|
||||
|
||||
Test(lexer, var_decl) {
|
||||
test_lexer(":=var", 3, "COLON", "ASSIGN", "VAR");
|
||||
}
|
||||
|
||||
Test(lexer, ident) {
|
||||
test_lexer("atrue salut! coucou32 az_za?", 4,
|
||||
"IDENT[atrue]", "IDENT[salut!]",
|
||||
"IDENT[coucou32]", "IDENT[az_za?]");
|
||||
}
|
||||
|
||||
Test(lexer, var_types) {
|
||||
test_lexer("int float bool string", 4,
|
||||
"TYPE[int]", "TYPE[float]", "TYPE[bool]", "TYPE[string]");
|
||||
}
|
||||
|
||||
Test(lexer, blocks) {
|
||||
test_lexer("{}", 2, "OBRACE", "CBRACE");
|
||||
}
|
||||
|
|
|
@ -108,3 +108,27 @@ Test(parser, cmp) {
|
|||
test_parser("ROOT(AND(LE(INT[0],INT[2]),GE(FLOAT[5.0],FLOAT[1.0])))",
|
||||
"0 <= 2 && 5.0 >= 1.0;");
|
||||
}
|
||||
|
||||
Test(parser, vardecl) {
|
||||
test_parser("ROOT(CONSTDECL[x](INT[3]))",
|
||||
"x := 3;");
|
||||
|
||||
test_parser("ROOT(CONSTDECL[x](TYPE[string],STRING[bim]))",
|
||||
"x : string = 'bim';");
|
||||
|
||||
test_parser("ROOT(VARDECL[x](INT[3]))",
|
||||
"var x := 3;");
|
||||
|
||||
test_parser("ROOT(VARDECL[x](TYPE[string],STRING[bim]))",
|
||||
"var x : string = 'bim';");
|
||||
}
|
||||
|
||||
Test(parser, assign) {
|
||||
test_parser("ROOT(ASSIGN(IDENT[x],INT[45]))",
|
||||
"x = 45;");
|
||||
}
|
||||
|
||||
Test(parser, block) {
|
||||
test_parser("ROOT(BLOCK(ASSIGN(IDENT[x],INT[2]),ADD(INT[1],INT[1])))",
|
||||
"{ x = 2; 1 + 1;}");
|
||||
}
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
#include <criterion/criterion.h>
|
||||
#include <syms.h>
|
||||
|
||||
Test(syms, register_var) {
|
||||
struct syms syms;
|
||||
syms_init(&syms);
|
||||
|
||||
syms_declare(&syms, "name", syms_try_type(&syms, TYPE_INT)->kind,
|
||||
SYM_IS_VAR, NULL);
|
||||
|
||||
struct syms_entry* entry = syms_try_get(&syms, "nope", NULL);
|
||||
cr_assert_eq(entry, NULL);
|
||||
|
||||
entry = syms_try_get(&syms, "name", NULL);
|
||||
cr_assert_neq(entry, NULL);
|
||||
|
||||
cr_assert_eq(entry->type, syms_try_type(&syms, TYPE_INT)->kind);
|
||||
|
||||
syms_free(&syms);
|
||||
}
|
|
@ -3,6 +3,7 @@
|
|||
#include <parser.h>
|
||||
#include <parser.h>
|
||||
#include <type_checker.h>
|
||||
#include <syms.h>
|
||||
|
||||
static void test_check_ok(char const* source)
|
||||
{
|
||||
|
@ -12,8 +13,11 @@ static void test_check_ok(char const* source)
|
|||
struct node* ast = parser_try_new_root(&parser, source);
|
||||
cr_assert_neq(ast, NULL);
|
||||
|
||||
struct syms syms;
|
||||
syms_init(&syms);
|
||||
|
||||
struct type_checker tc;
|
||||
type_checker_init(&tc);
|
||||
type_checker_init(&tc, &syms);
|
||||
|
||||
char buf[GUX_STR_SIZE];
|
||||
node_str(ast, buf, GUX_STR_SIZE);
|
||||
|
@ -22,6 +26,7 @@ static void test_check_ok(char const* source)
|
|||
"type checker failed: %s", buf);
|
||||
|
||||
type_checker_free(&tc);
|
||||
syms_free(&syms);
|
||||
|
||||
node_free(ast);
|
||||
free(ast);
|
||||
|
@ -36,11 +41,15 @@ static void test_check_ko(char const* source)
|
|||
struct node* ast = parser_try_new_root(&parser, source);
|
||||
cr_assert_neq(ast, NULL, "NULL: %s", source);
|
||||
|
||||
struct syms syms;
|
||||
syms_init(&syms);
|
||||
|
||||
struct type_checker tc;
|
||||
type_checker_init(&tc);
|
||||
type_checker_init(&tc, &syms);
|
||||
|
||||
cr_assert_neq(type_checker_check(&tc, ast), 0, "%s == null", source);
|
||||
|
||||
syms_free(&syms);
|
||||
type_checker_free(&tc);
|
||||
|
||||
node_free(ast);
|
||||
|
@ -153,3 +162,12 @@ Test(type_checker, cmp) {
|
|||
test_check_ok("5 <= 5;");
|
||||
test_check_ok("5.1 <= 5.2;");
|
||||
}
|
||||
|
||||
Test(type_checker, assign) {
|
||||
test_check_ok("var x := 3; x = 4;");
|
||||
test_check_ko("var x := 3; x = 4.0;");
|
||||
test_check_ko("var x := 3; x = true;");
|
||||
test_check_ko("var x := 3; x = 'salut';");
|
||||
test_check_ko("x := 3; x = 4;");
|
||||
test_check_ko("z = 4;");
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <ctype.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#define GUX_STR_SIZE 256
|
||||
#define GUX_ENUM_IDENT(X) X
|
||||
|
|
|
@ -8,7 +8,7 @@ do
|
|||
MSG=$(guxi $file 2>&1)
|
||||
RET=$?
|
||||
|
||||
echo -en "$file...\t"
|
||||
echo -en "$file -> "
|
||||
if [ $RET -eq 0 ]
|
||||
then
|
||||
echo -e "\e[32mpass\e[0m"
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
var x : int = 4;
|
||||
var y := 3.2;
|
||||
|
||||
assert x == 4;
|
||||
assert x != 5;
|
||||
assert y == 3.2;
|
||||
assert y != 3.1;
|
||||
|
||||
var a := (b := (c := 4));
|
||||
assert a == 4;
|
||||
assert b == 4;
|
||||
assert c == 4;
|
||||
|
||||
a = 9;
|
||||
assert a == 9;
|
||||
|
||||
var u := 0;
|
||||
var v := 0;
|
||||
|
||||
u = (v = 17);
|
||||
assert u == 17;
|
||||
assert v == 17;
|
||||
|
||||
# SCOPES
|
||||
# ======
|
||||
|
||||
var a0 := 42;
|
||||
|
||||
{
|
||||
assert a0 == 42;
|
||||
var a0 := 3;
|
||||
assert a0 == 3;
|
||||
|
||||
{
|
||||
var a0 := 777;
|
||||
assert a0 == 777;
|
||||
}
|
||||
|
||||
{
|
||||
var a0 := 19;
|
||||
assert a0 == 19;
|
||||
}
|
||||
|
||||
assert a0 == 3;
|
||||
}
|
||||
|
||||
assert a0 == 42;
|
60
vm/src/vm.c
60
vm/src/vm.c
|
@ -3,6 +3,7 @@
|
|||
#include "program.h"
|
||||
#include "type.h"
|
||||
#include "value.h"
|
||||
#include "vec.h"
|
||||
|
||||
void vm_init(struct vm* self)
|
||||
{
|
||||
|
@ -32,6 +33,8 @@ void vm_add_frame(struct vm* self)
|
|||
assert(self);
|
||||
self->stack[self->fp] = malloc(sizeof(struct frame));
|
||||
self->stack[self->fp]->sp = 0;
|
||||
vec_init(&self->stack[self->fp]->locals, 1);
|
||||
|
||||
self->fp++;
|
||||
}
|
||||
|
||||
|
@ -40,6 +43,9 @@ void vm_remove_frame(struct vm* self)
|
|||
assert(self);
|
||||
assert(self->fp > 0);
|
||||
|
||||
vec_free_elements(&self->stack[self->fp - 1]->locals);
|
||||
vec_free(&self->stack[self->fp - 1]->locals);
|
||||
|
||||
free(self->stack[self->fp - 1]);
|
||||
self->fp--;
|
||||
}
|
||||
|
@ -52,6 +58,48 @@ void vm_push(struct vm* self, int param)
|
|||
frame->sp++;
|
||||
}
|
||||
|
||||
int vm_load(struct vm* self, int addr)
|
||||
{
|
||||
assert(self);
|
||||
struct frame* frame = self->stack[self->fp - 1];
|
||||
|
||||
for (size_t i=0; i<frame->locals.size; i++)
|
||||
{
|
||||
struct local* loc = frame->locals.data[i];
|
||||
|
||||
if (loc->addr == addr)
|
||||
{
|
||||
return loc->stack_addr;
|
||||
}
|
||||
}
|
||||
|
||||
assert(0);
|
||||
}
|
||||
|
||||
void vm_store(struct vm* self, int addr)
|
||||
{
|
||||
assert(self);
|
||||
|
||||
struct frame* frame = self->stack[self->fp - 1];
|
||||
|
||||
for (size_t i=0; i<frame->locals.size; i++)
|
||||
{
|
||||
struct local* loc = frame->locals.data[i];
|
||||
|
||||
if (loc->addr == addr)
|
||||
{
|
||||
loc->stack_addr = frame->sp - 1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
struct local* loc = malloc(sizeof(struct local));
|
||||
loc->addr = addr;
|
||||
loc->stack_addr = frame->sp - 1;
|
||||
|
||||
vec_push(&frame->locals, loc);
|
||||
}
|
||||
|
||||
int vm_pop(struct vm* self)
|
||||
{
|
||||
assert(self);
|
||||
|
@ -76,6 +124,18 @@ int vm_exec(struct vm* self, struct program* program)
|
|||
|
||||
switch (opcode)
|
||||
{
|
||||
case OP_LOAD: {
|
||||
int stack_addr = vm_load(self, param);
|
||||
struct frame* myframe = self->stack[self->fp - 1];
|
||||
vm_push(self, myframe->stack[stack_addr]);
|
||||
self->pc++;
|
||||
} break;
|
||||
|
||||
case OP_STORE: {
|
||||
vm_store(self, param);
|
||||
self->pc++;
|
||||
} break;
|
||||
|
||||
case OP_PUSH: {
|
||||
vm_push(self, param);
|
||||
self->pc++;
|
||||
|
|
|
@ -7,9 +7,15 @@
|
|||
#define FRAME_DEPTH 256
|
||||
#define STACK_DEPTH 256
|
||||
|
||||
struct local {
|
||||
int addr;
|
||||
int stack_addr;
|
||||
};
|
||||
|
||||
struct frame {
|
||||
int stack[STACK_DEPTH];
|
||||
size_t sp;
|
||||
struct vec locals;
|
||||
};
|
||||
|
||||
struct vm {
|
||||
|
@ -28,6 +34,9 @@ void vm_remove_frame(struct vm* self);
|
|||
void vm_push(struct vm* self, int param);
|
||||
int vm_pop(struct vm* self);
|
||||
|
||||
int vm_load(struct vm* self, int addr);
|
||||
void vm_store(struct vm* self, int addr);
|
||||
|
||||
int vm_exec(struct vm* self, struct program* program);
|
||||
|
||||
size_t vm_str(struct vm* self, struct program* program,
|
||||
|
|
Loading…
Reference in New Issue