✨ int literals.
parent
e0502c7825
commit
1f71fce283
5
Makefile
5
Makefile
|
@ -5,7 +5,7 @@ build:
|
||||||
cmake --build build
|
cmake --build build
|
||||||
|
|
||||||
tests: build
|
tests: build
|
||||||
build/tests/skopy-tests \
|
@build/tests/skopy-tests \
|
||||||
&& echo -e "\e[32m--- all tests passed ---\e[0m" \
|
&& echo -e "\e[32m--- all tests passed ---\e[0m" \
|
||||||
|| echo -e "\e[31m--- some tests failed ---\e[0m"
|
|| echo -e "\e[31m--- some tests failed ---\e[0m"
|
||||||
|
|
||||||
|
@ -14,4 +14,5 @@ install: tests
|
||||||
|
|
||||||
check:
|
check:
|
||||||
@cppcheck --enable=all -q lib tests cli \
|
@cppcheck --enable=all -q lib tests cli \
|
||||||
--suppress=missingIncludeSystem
|
--suppress=missingIncludeSystem \
|
||||||
|
--suppress=missingInclude
|
||||||
|
|
18
cli/main.c
18
cli/main.c
|
@ -1,7 +1,21 @@
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <module.h>
|
||||||
|
#include <errors.h>
|
||||||
|
|
||||||
int main()
|
int main(int argc, char** argv)
|
||||||
{
|
{
|
||||||
printf("Hello World!\n");
|
if (argc == 2)
|
||||||
|
{
|
||||||
|
errors_init();
|
||||||
|
struct module module;
|
||||||
|
module_init(&module);
|
||||||
|
|
||||||
|
module_load_source(&module, argv[1]);
|
||||||
|
module_compile(&module);
|
||||||
|
|
||||||
|
module_free(&module);
|
||||||
|
errors_free();
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
ROOT ::= EXPR*
|
||||||
|
EXPR ::= BUILTIN
|
||||||
|
BUILTIN ::=
|
||||||
|
| int
|
|
@ -4,6 +4,24 @@ project(skopy-lib)
|
||||||
|
|
||||||
add_library(skopy-lib SHARED
|
add_library(skopy-lib SHARED
|
||||||
src/commons.c
|
src/commons.c
|
||||||
|
src/vec.c
|
||||||
|
src/str.c
|
||||||
|
|
||||||
|
src/token.c
|
||||||
|
src/lexer.c
|
||||||
|
|
||||||
|
src/node.c
|
||||||
|
src/parser.c
|
||||||
|
|
||||||
|
src/prog.c
|
||||||
|
src/value.c
|
||||||
|
|
||||||
|
src/compiler.c
|
||||||
|
src/state.c
|
||||||
|
src/exec.c
|
||||||
|
src/module.c
|
||||||
|
|
||||||
|
src/errors.c
|
||||||
)
|
)
|
||||||
|
|
||||||
file(GLOB_RECURSE
|
file(GLOB_RECURSE
|
||||||
|
|
|
@ -1,5 +1,25 @@
|
||||||
#ifndef SK_COMMONS_H
|
#ifndef SK_COMMONS_H
|
||||||
#define SK_COMMONS_H
|
#define SK_COMMONS_H
|
||||||
|
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
#include "str.h"
|
||||||
|
#include "vec.h"
|
||||||
|
#include "errors.h"
|
||||||
|
|
||||||
|
#define SK_ENUM_ENUM(X) X
|
||||||
|
#define SK_ENUM_STR(X) #X
|
||||||
|
|
||||||
|
#define SK_ENUM_H(PREFIX, DEF) \
|
||||||
|
typedef enum { DEF(SK_ENUM_ENUM) } PREFIX;\
|
||||||
|
extern char const* PREFIX ## Str []
|
||||||
|
|
||||||
|
#define SK_ENUM_C(PREFIX, DEF) \
|
||||||
|
char const* PREFIX ## Str [] = {DEF(SK_ENUM_STR)}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
#ifndef SK_COMPILER_H
|
||||||
|
#define SK_COMPILER_H
|
||||||
|
|
||||||
|
#include "commons.h"
|
||||||
|
#include "prog.h"
|
||||||
|
|
||||||
|
struct compiler
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
void compiler_init(struct compiler* self);
|
||||||
|
void compiler_free(struct compiler* self);
|
||||||
|
|
||||||
|
void compiler_compile(struct compiler* self,
|
||||||
|
struct node* node,
|
||||||
|
struct prog* prog);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
#ifndef SK_ERROR_H
|
||||||
|
#define SK_ERROR_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <vec.h>
|
||||||
|
#include <str.h>
|
||||||
|
|
||||||
|
struct error
|
||||||
|
{
|
||||||
|
int line;
|
||||||
|
char* msg;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern struct vec* errors;
|
||||||
|
|
||||||
|
void errors_init();
|
||||||
|
void errors_free();
|
||||||
|
|
||||||
|
void errors_push(int line, char const* format, ...);
|
||||||
|
bool errors_ok();
|
||||||
|
void errors_dump();
|
||||||
|
|
||||||
|
void error_init(struct error* self, int line, char* msg);
|
||||||
|
void error_free(struct error* self);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,20 @@
|
||||||
|
#ifndef SK_EXEC_H
|
||||||
|
#define SK_EXEC_H
|
||||||
|
|
||||||
|
#include "commons.h"
|
||||||
|
#include "state.h"
|
||||||
|
#include "prog.h"
|
||||||
|
|
||||||
|
struct exec
|
||||||
|
{
|
||||||
|
size_t pc;
|
||||||
|
};
|
||||||
|
|
||||||
|
void exec_init(struct exec* self);
|
||||||
|
void exec_free(struct exec* self);
|
||||||
|
|
||||||
|
void exec_execute(struct exec* self,
|
||||||
|
struct state* state,
|
||||||
|
struct prog* prog);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,30 @@
|
||||||
|
#ifndef SK_LEXER_H
|
||||||
|
#define SK_LEXER_H
|
||||||
|
|
||||||
|
#include "commons.h"
|
||||||
|
#include "token.h"
|
||||||
|
|
||||||
|
struct context
|
||||||
|
{
|
||||||
|
int line;
|
||||||
|
size_t cursor;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct lexer
|
||||||
|
{
|
||||||
|
char* source;
|
||||||
|
size_t len;
|
||||||
|
struct context context;
|
||||||
|
};
|
||||||
|
|
||||||
|
void lexer_init(struct lexer* self, char const* source);
|
||||||
|
void lexer_free(struct lexer* self);
|
||||||
|
|
||||||
|
void lexer_skip_spaces(struct lexer* self);
|
||||||
|
|
||||||
|
bool lexer_next_is(struct lexer* self, TokenKind kind);
|
||||||
|
|
||||||
|
struct token* lexer_try_new_next(struct lexer* self);
|
||||||
|
struct token* lexer_try_scan_int(struct lexer* self);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,21 @@
|
||||||
|
#ifndef SK_MODULE_H
|
||||||
|
#define SK_MODULE_H
|
||||||
|
|
||||||
|
#include "commons.h"
|
||||||
|
#include "prog.h"
|
||||||
|
|
||||||
|
struct module
|
||||||
|
{
|
||||||
|
struct str source;
|
||||||
|
struct prog prog;
|
||||||
|
};
|
||||||
|
|
||||||
|
void module_init(struct module* self);
|
||||||
|
void module_free(struct module* self);
|
||||||
|
|
||||||
|
void module_load_source(struct module* self,
|
||||||
|
char const* path);
|
||||||
|
|
||||||
|
void module_compile(struct module* self);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,27 @@
|
||||||
|
#ifndef SK_NODE_H
|
||||||
|
#define SK_NODE_H
|
||||||
|
|
||||||
|
#include "commons.h"
|
||||||
|
|
||||||
|
#define NODE_KIND(G) \
|
||||||
|
G(NODE_ROOT), \
|
||||||
|
G(NODE_INT)
|
||||||
|
|
||||||
|
SK_ENUM_H(NodeKind, NODE_KIND);
|
||||||
|
|
||||||
|
struct node
|
||||||
|
{
|
||||||
|
NodeKind kind;
|
||||||
|
struct vec children;
|
||||||
|
struct token* token;
|
||||||
|
};
|
||||||
|
|
||||||
|
void node_init(struct node* self,
|
||||||
|
NodeKind kind,
|
||||||
|
struct token* token);
|
||||||
|
void node_free(struct node* self);
|
||||||
|
|
||||||
|
void node_push_new_child(struct node* self, struct node* child);
|
||||||
|
|
||||||
|
void node_str(struct node* self, struct str* dest);
|
||||||
|
#endif
|
|
@ -0,0 +1,25 @@
|
||||||
|
#ifndef SK_PARSER_H
|
||||||
|
#define SK_PARSER_H
|
||||||
|
|
||||||
|
#include "commons.h"
|
||||||
|
#include "node.h"
|
||||||
|
#include "lexer.h"
|
||||||
|
|
||||||
|
struct parser
|
||||||
|
{
|
||||||
|
struct lexer lexer;
|
||||||
|
};
|
||||||
|
|
||||||
|
void parser_init(struct parser* self, char const* source);
|
||||||
|
void parser_free(struct parser* self);
|
||||||
|
|
||||||
|
struct node* parser_try(struct parser* self,
|
||||||
|
struct node* (*rule)(struct parser*));
|
||||||
|
|
||||||
|
struct node* parser_try_parse(struct parser* self);
|
||||||
|
struct node* parser_try_root(struct parser* self);
|
||||||
|
struct node* parser_try_expr(struct parser* self);
|
||||||
|
struct node* parser_try_builtin(struct parser* self);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
#ifndef SK_PROG_H
|
||||||
|
#define SK_PROG_H
|
||||||
|
|
||||||
|
#include "commons.h"
|
||||||
|
#include "value.h"
|
||||||
|
|
||||||
|
#define SK_NO_PARAM (-1)
|
||||||
|
#define OPCODE(G) \
|
||||||
|
G(OP_PUSH)
|
||||||
|
|
||||||
|
SK_ENUM_H(Opcode, OPCODE);
|
||||||
|
|
||||||
|
struct prog
|
||||||
|
{
|
||||||
|
struct vec opcodes;
|
||||||
|
struct vec params;
|
||||||
|
struct vec constants;
|
||||||
|
};
|
||||||
|
|
||||||
|
void prog_init(struct prog* self);
|
||||||
|
void prog_free(struct prog* self);
|
||||||
|
|
||||||
|
size_t prog_add_instr(struct prog* self,
|
||||||
|
Opcode opcode,
|
||||||
|
size_t param);
|
||||||
|
|
||||||
|
size_t prog_add_constant(struct prog* self,
|
||||||
|
struct value* new_value);
|
||||||
|
#endif
|
|
@ -0,0 +1,47 @@
|
||||||
|
#ifndef SK_STATE_H
|
||||||
|
#define SK_STATE_H
|
||||||
|
|
||||||
|
#include "commons.h"
|
||||||
|
#include "value.h"
|
||||||
|
#define SK size_t
|
||||||
|
|
||||||
|
struct local
|
||||||
|
{
|
||||||
|
size_t addr;
|
||||||
|
struct value* value;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct frame
|
||||||
|
{
|
||||||
|
struct vec locals;
|
||||||
|
struct vec stack;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct state
|
||||||
|
{
|
||||||
|
struct vec frames;
|
||||||
|
size_t addr;
|
||||||
|
};
|
||||||
|
|
||||||
|
void local_init(struct local* self,
|
||||||
|
size_t addr,
|
||||||
|
struct value* new_value);
|
||||||
|
|
||||||
|
void local_free(struct local* self);
|
||||||
|
|
||||||
|
void frame_init(struct frame* self);
|
||||||
|
void frame_free(struct frame* self);
|
||||||
|
|
||||||
|
void state_init(struct state* self);
|
||||||
|
void state_free(struct state* self);
|
||||||
|
|
||||||
|
struct frame* state_frame(struct state* self);
|
||||||
|
void state_push_frame(struct state* self);
|
||||||
|
|
||||||
|
bool state_has_top(struct state* self);
|
||||||
|
SK state_top(struct state* self);
|
||||||
|
struct value* state_try_get_value(struct state* self, SK value);
|
||||||
|
|
||||||
|
SK state_push_int(struct state* self, int integer);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,24 @@
|
||||||
|
#ifndef SK_STR_H
|
||||||
|
#define SK_STR_H
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
struct str
|
||||||
|
{
|
||||||
|
size_t size;
|
||||||
|
size_t capacity;
|
||||||
|
char* value;
|
||||||
|
};
|
||||||
|
|
||||||
|
void str_init(struct str* self);
|
||||||
|
void str_free(struct str* self);
|
||||||
|
|
||||||
|
void str_push(struct str* self, char c);
|
||||||
|
void str_extend(struct str* self, char* src);
|
||||||
|
void str_format(struct str* self, char const* format, ...);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,27 @@
|
||||||
|
#ifndef SK_TOKEN_H
|
||||||
|
#define SK_TOKEN_H
|
||||||
|
|
||||||
|
#include "commons.h"
|
||||||
|
|
||||||
|
#define TOKEN_KIND(G) \
|
||||||
|
G(TOKEN_ROOT), \
|
||||||
|
G(TOKEN_INT)
|
||||||
|
|
||||||
|
SK_ENUM_H(TokenKind, TOKEN_KIND);
|
||||||
|
|
||||||
|
struct token
|
||||||
|
{
|
||||||
|
TokenKind kind;
|
||||||
|
char* value;
|
||||||
|
int line;
|
||||||
|
};
|
||||||
|
|
||||||
|
void token_init(struct token* self,
|
||||||
|
TokenKind kind,
|
||||||
|
char const* value,
|
||||||
|
int line);
|
||||||
|
void token_free(struct token* self);
|
||||||
|
|
||||||
|
void token_str(struct token* self, struct str* dest);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,32 @@
|
||||||
|
#ifndef SK_VALUE_H
|
||||||
|
#define SK_VALUE_H
|
||||||
|
|
||||||
|
#include "commons.h"
|
||||||
|
#include "node.h"
|
||||||
|
|
||||||
|
#define TYPE_KIND(G) \
|
||||||
|
G(TYPE_INT)
|
||||||
|
|
||||||
|
SK_ENUM_H(TypeKind, TYPE_KIND);
|
||||||
|
|
||||||
|
union val
|
||||||
|
{
|
||||||
|
int integer;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct value
|
||||||
|
{
|
||||||
|
TypeKind type;
|
||||||
|
union val val;
|
||||||
|
};
|
||||||
|
|
||||||
|
void value_init(struct value* self,
|
||||||
|
TypeKind type,
|
||||||
|
union val val);
|
||||||
|
|
||||||
|
void value_free(struct value* self);
|
||||||
|
|
||||||
|
void value_str(struct value* self, struct str* dest);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
#ifndef SK_VEC_H
|
||||||
|
#define SK_VEC_H
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
struct vec
|
||||||
|
{
|
||||||
|
size_t size;
|
||||||
|
size_t capacity;
|
||||||
|
void** data;
|
||||||
|
};
|
||||||
|
|
||||||
|
void vec_init(struct vec* self);
|
||||||
|
void vec_free_elements(struct vec* self,
|
||||||
|
void (*destructor)(void*));
|
||||||
|
void vec_free(struct vec* self);
|
||||||
|
|
||||||
|
void vec_push(struct vec* self, void* element);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
#include "compiler.h"
|
||||||
|
#include "token.h"
|
||||||
|
|
||||||
|
void compiler_init(struct compiler* self)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
void compiler_free(struct compiler* self)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
void compiler_compile(struct compiler* self,
|
||||||
|
struct node* node,
|
||||||
|
struct prog* prog)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
assert(node);
|
||||||
|
assert(prog);
|
||||||
|
|
||||||
|
switch (node->kind)
|
||||||
|
{
|
||||||
|
case NODE_ROOT: {
|
||||||
|
for (size_t i=0; i<node->children.size; i++)
|
||||||
|
{
|
||||||
|
compiler_compile(self, node->children.data[i],
|
||||||
|
prog);
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case NODE_INT: {
|
||||||
|
struct value* value = malloc(sizeof(struct value));
|
||||||
|
union val val;
|
||||||
|
val.integer = atoi(node->token->value);
|
||||||
|
value_init(value, TYPE_INT, val);
|
||||||
|
|
||||||
|
size_t idx = prog_add_constant(prog, value);
|
||||||
|
prog_add_instr(prog, OP_PUSH, idx);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
default: {
|
||||||
|
fprintf(stderr, "cannot compile node '%s'\n",
|
||||||
|
NodeKindStr[node->kind]);
|
||||||
|
abort();
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
#include "errors.h"
|
||||||
|
|
||||||
|
struct vec* errors = NULL;
|
||||||
|
|
||||||
|
void errors_init()
|
||||||
|
{
|
||||||
|
errors = malloc(sizeof(struct vec));
|
||||||
|
vec_init(errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
void errors_free()
|
||||||
|
{
|
||||||
|
vec_free_elements(errors, (void*) error_free);
|
||||||
|
vec_free(errors);
|
||||||
|
free(errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
void errors_push(int line, char const* format, ...)
|
||||||
|
{
|
||||||
|
va_list lst;
|
||||||
|
va_start(lst, format);
|
||||||
|
|
||||||
|
size_t const sz = 4096;
|
||||||
|
char buffer[sz];
|
||||||
|
|
||||||
|
vsnprintf(buffer, sz, format, lst);
|
||||||
|
|
||||||
|
struct error* err = malloc(sizeof(struct error));
|
||||||
|
error_init(err, line, buffer);
|
||||||
|
|
||||||
|
vec_push(errors, err);
|
||||||
|
|
||||||
|
va_end(lst);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool errors_ok()
|
||||||
|
{
|
||||||
|
return errors->size == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void errors_dump()
|
||||||
|
{
|
||||||
|
for (size_t i=0; i<errors->size; i++)
|
||||||
|
{
|
||||||
|
struct error const* err = errors->data[i];
|
||||||
|
|
||||||
|
fprintf(stderr, "[ERR:%d] %s\n", err->line, err->msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void error_init(struct error* self, int line, char* msg)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
assert(msg);
|
||||||
|
|
||||||
|
self->line = line;
|
||||||
|
self->msg = strdup(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void error_free(struct error* self)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
free(self->msg);
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
#include "exec.h"
|
||||||
|
|
||||||
|
void exec_init(struct exec* self)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
self->pc = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void exec_free(struct exec* self)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
void exec_execute(struct exec* self,
|
||||||
|
struct state* state,
|
||||||
|
struct prog* prog)
|
||||||
|
{
|
||||||
|
while (self->pc < prog->opcodes.size)
|
||||||
|
{
|
||||||
|
Opcode opcode = (size_t) prog->opcodes.data[self->pc];
|
||||||
|
size_t param = (size_t) prog->params.data[self->pc];
|
||||||
|
|
||||||
|
switch (opcode)
|
||||||
|
{
|
||||||
|
case OP_PUSH: {
|
||||||
|
struct value* constant = prog->constants.data[param];
|
||||||
|
switch (constant->type)
|
||||||
|
{
|
||||||
|
case TYPE_INT: {
|
||||||
|
state_push_int(state, constant->val.integer);
|
||||||
|
} break;
|
||||||
|
default: {
|
||||||
|
fprintf(stderr, "cannot push value '%s'\n",
|
||||||
|
TypeKindStr[constant->type]);
|
||||||
|
abort();
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
self->pc++;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
default: {
|
||||||
|
fprintf(stderr, "cannot execute opcode '%s'\n",
|
||||||
|
OpcodeStr[opcode]);
|
||||||
|
abort();
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,127 @@
|
||||||
|
#include "lexer.h"
|
||||||
|
|
||||||
|
void lexer_init(struct lexer* self, char const* source)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
self->source = strdup(source);
|
||||||
|
self->len = strlen(self->source);
|
||||||
|
self->context.line = 1;
|
||||||
|
self->context.cursor = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void lexer_free(struct lexer* self)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
free(self->source);
|
||||||
|
}
|
||||||
|
|
||||||
|
void lexer_skip_spaces(struct lexer* self)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
|
||||||
|
while (self->context.cursor < self->len
|
||||||
|
&& isspace(self->source[self->context.cursor]))
|
||||||
|
{
|
||||||
|
if (self->source[self->context.cursor] == '\n')
|
||||||
|
{
|
||||||
|
self->context.line++;
|
||||||
|
}
|
||||||
|
|
||||||
|
self->context.cursor++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool lexer_next_is(struct lexer* self, TokenKind kind)
|
||||||
|
{
|
||||||
|
struct context ctx = self->context;
|
||||||
|
|
||||||
|
struct token* tok = lexer_try_new_next(self);
|
||||||
|
|
||||||
|
if (!tok)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool res = tok->kind == kind;
|
||||||
|
token_free(tok);
|
||||||
|
free(tok);
|
||||||
|
|
||||||
|
self->context = ctx;
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct token* lexer_try_new_next(struct lexer* self)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
|
||||||
|
if (!errors_ok())
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
lexer_skip_spaces(self);
|
||||||
|
|
||||||
|
struct token* tok = NULL;
|
||||||
|
|
||||||
|
if ( (tok=lexer_try_scan_int(self)) )
|
||||||
|
{
|
||||||
|
return tok;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self->context.cursor < self->len)
|
||||||
|
{
|
||||||
|
struct str str;
|
||||||
|
str_init(&str);
|
||||||
|
|
||||||
|
while (self->context.cursor < self->len
|
||||||
|
&& !isspace(self->source[self->context.cursor]))
|
||||||
|
{
|
||||||
|
str_push(&str, self->source[self->context.cursor]);
|
||||||
|
self->context.cursor++;
|
||||||
|
}
|
||||||
|
|
||||||
|
errors_push(self->context.line, "unknown symbol '%s'",
|
||||||
|
str.value);
|
||||||
|
str_free(&str);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct token* lexer_try_scan_int(struct lexer* self)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
size_t cursor = self->context.cursor;
|
||||||
|
struct str value;
|
||||||
|
str_init(&value);
|
||||||
|
|
||||||
|
if (cursor < self->len
|
||||||
|
&& self->source[cursor] == '-')
|
||||||
|
{
|
||||||
|
str_push(&value, self->source[cursor]);
|
||||||
|
cursor++;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (cursor < self->len
|
||||||
|
&& isdigit(self->source[cursor]))
|
||||||
|
{
|
||||||
|
str_push(&value, self->source[cursor]);
|
||||||
|
cursor++;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct token* tok = NULL;
|
||||||
|
|
||||||
|
if (value.size > 0)
|
||||||
|
{
|
||||||
|
tok = malloc(sizeof(struct token));
|
||||||
|
token_init(tok, TOKEN_INT,
|
||||||
|
value.value, self->context.line);
|
||||||
|
|
||||||
|
self->context.cursor = cursor;
|
||||||
|
}
|
||||||
|
|
||||||
|
str_free(&value);
|
||||||
|
return tok;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,89 @@
|
||||||
|
#include "module.h"
|
||||||
|
#include "parser.h"
|
||||||
|
#include "compiler.h"
|
||||||
|
#include "state.h"
|
||||||
|
#include "exec.h"
|
||||||
|
|
||||||
|
void module_init(struct module* self)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
str_init(&self->source);
|
||||||
|
prog_init(&self->prog);
|
||||||
|
}
|
||||||
|
|
||||||
|
void module_free(struct module* self)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
str_free(&self->source);
|
||||||
|
prog_free(&self->prog);
|
||||||
|
}
|
||||||
|
|
||||||
|
void module_load_source(struct module* self,
|
||||||
|
char const* path)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
FILE* file = fopen(path, "r");
|
||||||
|
assert(file);
|
||||||
|
|
||||||
|
char buf;
|
||||||
|
|
||||||
|
while ( fread(&buf, 1, sizeof(char), file) > 0 )
|
||||||
|
{
|
||||||
|
str_push(&self->source, buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
void module_compile(struct module* self)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
struct parser parser;
|
||||||
|
|
||||||
|
parser_init(&parser, self->source.value);
|
||||||
|
struct node* root = parser_try_parse(&parser);
|
||||||
|
|
||||||
|
if (!errors_ok())
|
||||||
|
{
|
||||||
|
errors_dump();
|
||||||
|
goto free_node;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct compiler compiler;
|
||||||
|
compiler_init(&compiler);
|
||||||
|
|
||||||
|
compiler_compile(&compiler, root, &self->prog);
|
||||||
|
|
||||||
|
struct exec exec;
|
||||||
|
exec_init(&exec);
|
||||||
|
|
||||||
|
struct state state;
|
||||||
|
state_init(&state);
|
||||||
|
|
||||||
|
exec_execute(&exec, &state, &self->prog);
|
||||||
|
|
||||||
|
if (state_has_top(&state))
|
||||||
|
{
|
||||||
|
struct value* value = state_try_get_value(
|
||||||
|
&state,
|
||||||
|
state_top(&state)
|
||||||
|
);
|
||||||
|
|
||||||
|
assert(value);
|
||||||
|
|
||||||
|
struct str str;
|
||||||
|
str_init(&str);
|
||||||
|
value_str(value, &str);
|
||||||
|
printf("%s\n", str.value);
|
||||||
|
str_free(&str);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free
|
||||||
|
state_free(&state);
|
||||||
|
exec_free(&exec);
|
||||||
|
compiler_free(&compiler);
|
||||||
|
free_node:
|
||||||
|
node_free(root);
|
||||||
|
free(root);
|
||||||
|
parser_free(&parser);
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
#include "node.h"
|
||||||
|
#include "token.h"
|
||||||
|
|
||||||
|
SK_ENUM_C(NodeKind, NODE_KIND);
|
||||||
|
|
||||||
|
void node_init(struct node* self,
|
||||||
|
NodeKind kind,
|
||||||
|
struct token* token)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
self->kind = kind;
|
||||||
|
vec_init(&self->children);
|
||||||
|
self->token = token;
|
||||||
|
}
|
||||||
|
|
||||||
|
void node_free(struct node* self)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
vec_free_elements(&self->children, (void*) node_free);
|
||||||
|
vec_free(&self->children);
|
||||||
|
|
||||||
|
if (self->token)
|
||||||
|
{
|
||||||
|
token_free(self->token);
|
||||||
|
free(self->token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void node_push_new_child(struct node* self, struct node* child)
|
||||||
|
{
|
||||||
|
vec_push(&self->children, child);
|
||||||
|
}
|
||||||
|
|
||||||
|
void node_str(struct node* self, struct str* dest)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
assert(dest);
|
||||||
|
assert(self->token);
|
||||||
|
str_format(dest, "%s",
|
||||||
|
NodeKindStr[self->kind] + strlen("NODE_"));
|
||||||
|
|
||||||
|
if (strcmp(self->token->value, "") != 0)
|
||||||
|
{
|
||||||
|
str_format(dest, "[%s]", self->token->value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self->children.size > 0)
|
||||||
|
{
|
||||||
|
str_extend(dest, "(");
|
||||||
|
for (size_t i=0; i<self->children.size; i++)
|
||||||
|
{
|
||||||
|
if (i > 0) { str_push(dest, ','); }
|
||||||
|
node_str(self->children.data[i], dest);
|
||||||
|
}
|
||||||
|
str_extend(dest, ")");
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,83 @@
|
||||||
|
#include "parser.h"
|
||||||
|
|
||||||
|
#define SK_TRY(RULE) parser_try(self, RULE)
|
||||||
|
|
||||||
|
void parser_init(struct parser* self, char const* source)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
lexer_init(&self->lexer, source);
|
||||||
|
}
|
||||||
|
|
||||||
|
void parser_free(struct parser* self)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
lexer_free(&self->lexer);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct node* parser_try(struct parser* self,
|
||||||
|
struct node* (*rule)(struct parser*))
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
assert(rule);
|
||||||
|
|
||||||
|
struct context ctx = self->lexer.context;
|
||||||
|
|
||||||
|
struct node* res = (*rule)(self);
|
||||||
|
|
||||||
|
if (res == NULL)
|
||||||
|
{
|
||||||
|
self->lexer.context = ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct node* parser_try_parse(struct parser* self)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
return SK_TRY(parser_try_root);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct node* parser_try_root(struct parser* self)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
struct node* node = malloc(sizeof(struct node));
|
||||||
|
struct token* tok = malloc(sizeof(struct token));
|
||||||
|
token_init(tok,
|
||||||
|
TOKEN_ROOT,
|
||||||
|
"",
|
||||||
|
self->lexer.context.line);
|
||||||
|
|
||||||
|
node_init(node, NODE_ROOT, tok);
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
struct node* expr = SK_TRY(parser_try_expr);
|
||||||
|
if (!expr) { break; }
|
||||||
|
|
||||||
|
node_push_new_child(node, expr);
|
||||||
|
}
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct node* parser_try_expr(struct parser* self)
|
||||||
|
{
|
||||||
|
return SK_TRY(parser_try_builtin);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct node* parser_try_builtin(struct parser* self)
|
||||||
|
{
|
||||||
|
if (lexer_next_is(&self->lexer, TOKEN_INT))
|
||||||
|
{
|
||||||
|
struct node* node = malloc(sizeof(struct node));
|
||||||
|
|
||||||
|
node_init(node,
|
||||||
|
NODE_INT,
|
||||||
|
lexer_try_new_next(&self->lexer));
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
#include "prog.h"
|
||||||
|
#include "value.h"
|
||||||
|
|
||||||
|
SK_ENUM_C(Opcode, OPCODE);
|
||||||
|
|
||||||
|
void prog_init(struct prog* self)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
vec_init(&self->opcodes);
|
||||||
|
vec_init(&self->params);
|
||||||
|
vec_init(&self->constants);
|
||||||
|
}
|
||||||
|
|
||||||
|
void prog_free(struct prog* self)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
vec_free(&self->opcodes);
|
||||||
|
vec_free(&self->params);
|
||||||
|
vec_free_elements(&self->constants, (void*)value_free);
|
||||||
|
vec_free(&self->constants);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t prog_add_instr(struct prog* self,
|
||||||
|
Opcode opcode,
|
||||||
|
size_t param)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
size_t addr = self->opcodes.size;
|
||||||
|
vec_push(&self->opcodes, (void*) opcode);
|
||||||
|
vec_push(&self->params, (void*) param);
|
||||||
|
|
||||||
|
return addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t prog_add_constant(struct prog* self,
|
||||||
|
struct value* new_value)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
vec_push(&self->constants, new_value);
|
||||||
|
|
||||||
|
return self->constants.size - 1;
|
||||||
|
}
|
|
@ -0,0 +1,117 @@
|
||||||
|
#include "state.h"
|
||||||
|
|
||||||
|
void local_init(struct local* self,
|
||||||
|
size_t addr,
|
||||||
|
struct value* new_value)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
assert(new_value);
|
||||||
|
|
||||||
|
self->addr = addr;
|
||||||
|
self->value = new_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void local_free(struct local* self)
|
||||||
|
{
|
||||||
|
value_free(self->value);
|
||||||
|
free(self->value);
|
||||||
|
self->value = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void frame_init(struct frame* self)
|
||||||
|
{
|
||||||
|
vec_init(&self->locals);
|
||||||
|
vec_init(&self->stack);
|
||||||
|
}
|
||||||
|
|
||||||
|
void frame_free(struct frame* self)
|
||||||
|
{
|
||||||
|
vec_free_elements(&self->locals, (void*) local_free);
|
||||||
|
vec_free(&self->locals);
|
||||||
|
vec_free(&self->stack);
|
||||||
|
}
|
||||||
|
|
||||||
|
void state_init(struct state* self)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
vec_init(&self->frames);
|
||||||
|
self->addr = 1;
|
||||||
|
state_push_frame(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
void state_free(struct state* self)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
vec_free_elements(&self->frames, (void*) frame_free);
|
||||||
|
vec_free(&self->frames);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct frame* state_frame(struct state* self)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
assert(self->frames.size > 0);
|
||||||
|
|
||||||
|
return self->frames.data[self->frames.size - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
void state_push_frame(struct state* self)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
struct frame* frame = malloc(sizeof(struct frame));
|
||||||
|
frame_init(frame);
|
||||||
|
|
||||||
|
vec_push(&self->frames, frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool state_has_top(struct state* self)
|
||||||
|
{
|
||||||
|
struct frame* frame = state_frame(self);
|
||||||
|
return frame->stack.size > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
SK state_top(struct state* self)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
struct frame* frame = state_frame(self);
|
||||||
|
assert(frame->stack.size > 0);
|
||||||
|
|
||||||
|
return (SK) frame->stack.data[frame->stack.size - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
struct value* state_try_get_value(struct state* self, SK value)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
struct frame* frame = state_frame(self);
|
||||||
|
|
||||||
|
for (size_t i=0; i<frame->locals.size; i++)
|
||||||
|
{
|
||||||
|
struct local* local = frame->locals.data[i];
|
||||||
|
|
||||||
|
if (local->addr == value)
|
||||||
|
{
|
||||||
|
return local->value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
SK state_push_int(struct state* self, int integer)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
|
||||||
|
struct value* value = malloc(sizeof(struct value));
|
||||||
|
union val val;
|
||||||
|
val.integer = integer;
|
||||||
|
value_init(value, TYPE_INT, val);
|
||||||
|
|
||||||
|
struct frame* frame = state_frame(self);
|
||||||
|
struct local* local = malloc(sizeof(struct local));
|
||||||
|
local_init(local, self->addr, value);
|
||||||
|
|
||||||
|
vec_push(&frame->locals, local);
|
||||||
|
vec_push(&frame->stack, (void*) self->addr);
|
||||||
|
|
||||||
|
self->addr++;
|
||||||
|
return self->addr - 1;
|
||||||
|
}
|
|
@ -0,0 +1,72 @@
|
||||||
|
#include "str.h"
|
||||||
|
|
||||||
|
void str_init(struct str* self)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
self->size = 0;
|
||||||
|
self->capacity = 0;
|
||||||
|
self->value = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void str_free(struct str* self)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
|
||||||
|
if (self->value)
|
||||||
|
{
|
||||||
|
free(self->value);
|
||||||
|
}
|
||||||
|
|
||||||
|
self->size = 0;
|
||||||
|
self->capacity = 0;
|
||||||
|
self->value = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void str_push(struct str* self, char c)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
|
||||||
|
if (self->capacity == 0)
|
||||||
|
{
|
||||||
|
self->capacity = 2;
|
||||||
|
self->value = malloc(sizeof(char) * self->capacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self->size + 2 >= self->capacity)
|
||||||
|
{
|
||||||
|
self->capacity *= 2;
|
||||||
|
char* buffer = realloc(self->value,
|
||||||
|
sizeof(char) * self->capacity);
|
||||||
|
assert(buffer);
|
||||||
|
self->value = buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
self->value[self->size] = c;
|
||||||
|
self->value[self->size + 1] = '\0';
|
||||||
|
self->size++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void str_extend(struct str* self, char* src)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
assert(src);
|
||||||
|
size_t sz = strlen(src);
|
||||||
|
|
||||||
|
for (size_t i=0; i<sz; i++)
|
||||||
|
{
|
||||||
|
str_push(self, src[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void str_format(struct str* self, char const* format, ...)
|
||||||
|
{
|
||||||
|
va_list lst;
|
||||||
|
va_start(lst, format);
|
||||||
|
size_t SZ = 4096;
|
||||||
|
char value[SZ];
|
||||||
|
vsnprintf(value, SZ, format, lst);
|
||||||
|
|
||||||
|
str_extend(self, value);
|
||||||
|
|
||||||
|
va_end(lst);
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
#include "token.h"
|
||||||
|
|
||||||
|
SK_ENUM_C(TokenKind, TOKEN_KIND);
|
||||||
|
|
||||||
|
void token_init(struct token* self,
|
||||||
|
TokenKind kind,
|
||||||
|
char const* value,
|
||||||
|
int line)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
self->kind = kind;
|
||||||
|
self->value = strdup(value);
|
||||||
|
self->line = line;
|
||||||
|
}
|
||||||
|
|
||||||
|
void token_free(struct token* self)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
free(self->value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void token_str(struct token* self, struct str* dest)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
assert(dest);
|
||||||
|
|
||||||
|
if (self->value == NULL || strcmp(self->value, "") == 0)
|
||||||
|
{
|
||||||
|
str_format(dest, "%s",
|
||||||
|
TokenKindStr[self->kind] + strlen("TOKEN_"));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
str_format(dest, "%s[%s]",
|
||||||
|
TokenKindStr[self->kind] + strlen("TOKEN_"),
|
||||||
|
self->value);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
#include "value.h"
|
||||||
|
|
||||||
|
SK_ENUM_C(TypeKind, TYPE_KIND);
|
||||||
|
|
||||||
|
void value_init(struct value* self,
|
||||||
|
TypeKind type,
|
||||||
|
union val val)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
self->type = type;
|
||||||
|
self->val = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
void value_free(struct value* self)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
void value_str(struct value* self, struct str* dest)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
assert(dest);
|
||||||
|
|
||||||
|
switch (self->type)
|
||||||
|
{
|
||||||
|
case TYPE_INT: {
|
||||||
|
str_format(dest, "%d", self->val.integer);
|
||||||
|
} break;
|
||||||
|
default: {
|
||||||
|
fprintf(stderr, "cannot get value string of '%s'\n",
|
||||||
|
TypeKindStr[self->type]);
|
||||||
|
abort();
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,59 @@
|
||||||
|
#include "vec.h"
|
||||||
|
|
||||||
|
void vec_init(struct vec* self)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
self->size = 0;
|
||||||
|
self->capacity = 0;
|
||||||
|
self->data = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void vec_free_elements(struct vec* self,
|
||||||
|
void (*destructor)(void*))
|
||||||
|
{
|
||||||
|
for (size_t i=0; i<self->size; i++)
|
||||||
|
{
|
||||||
|
if (destructor)
|
||||||
|
{
|
||||||
|
(*destructor)(self->data[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(self->data[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void vec_free(struct vec* self)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
|
||||||
|
free(self->data);
|
||||||
|
|
||||||
|
self->size = 0;
|
||||||
|
self->capacity = 0;
|
||||||
|
self->data = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void vec_push(struct vec* self, void* element)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
|
||||||
|
if (self->data == NULL)
|
||||||
|
{
|
||||||
|
self->capacity = 2;
|
||||||
|
self->data = malloc(sizeof(void*) * self->capacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self->size >= self->capacity)
|
||||||
|
{
|
||||||
|
self->capacity *= 2;
|
||||||
|
void** buffer = realloc(
|
||||||
|
self->data,
|
||||||
|
sizeof(void*) * self->capacity
|
||||||
|
);
|
||||||
|
assert(buffer);
|
||||||
|
self->data = buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
self->data[self->size] = element;
|
||||||
|
self->size++;
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
#ifndef SK_TEST_LEXER_H
|
||||||
|
#define SK_TEST_LEXER_H
|
||||||
|
|
||||||
|
#include <CUnit/CUnit.h>
|
||||||
|
#include <lexer.h>
|
||||||
|
#include <token.h>
|
||||||
|
|
||||||
|
static void test_lexer(char const* source, int count, ...)
|
||||||
|
{
|
||||||
|
struct lexer lexer;
|
||||||
|
lexer_init(&lexer, source);
|
||||||
|
|
||||||
|
va_list lst;
|
||||||
|
va_start(lst, count);
|
||||||
|
|
||||||
|
for (int i=0; i<count; i++)
|
||||||
|
{
|
||||||
|
struct token* tok = lexer_try_new_next(&lexer);
|
||||||
|
CU_ASSERT_FATAL(tok != NULL);
|
||||||
|
struct str tok_str;
|
||||||
|
str_init(&tok_str);
|
||||||
|
token_str(tok, &tok_str);
|
||||||
|
char* oracle = va_arg(lst, char*);
|
||||||
|
if (strcmp(oracle, tok_str.value) != 0)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s != %s\n", oracle, tok_str.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
CU_ASSERT_STRING_EQUAL(tok_str.value, oracle);
|
||||||
|
str_free(&tok_str);
|
||||||
|
token_free(tok);
|
||||||
|
free(tok);
|
||||||
|
}
|
||||||
|
|
||||||
|
va_end(lst);
|
||||||
|
lexer_free(&lexer);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_lexer_int()
|
||||||
|
{
|
||||||
|
test_lexer(" 4 -23 720 ", 3,
|
||||||
|
"INT[4]",
|
||||||
|
"INT[-23]",
|
||||||
|
"INT[720]"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void register_lexer()
|
||||||
|
{
|
||||||
|
CU_pSuite suite = CU_add_suite("Lexer", 0, 0);
|
||||||
|
CU_add_test(suite, "Integers", test_lexer_int);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
14
tests/main.c
14
tests/main.c
|
@ -1,17 +1,15 @@
|
||||||
#include <CUnit/CUnit.h>
|
#include <CUnit/CUnit.h>
|
||||||
#include <CUnit/Basic.h>
|
#include <CUnit/Basic.h>
|
||||||
|
#include "lexer.h"
|
||||||
void test_trivial()
|
#include "parser.h"
|
||||||
{
|
|
||||||
CU_ASSERT(1 + 1 == 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
|
errors_init();
|
||||||
CU_initialize_registry();
|
CU_initialize_registry();
|
||||||
|
|
||||||
CU_pSuite suite = CU_add_suite("First suite", 0, 0);
|
register_lexer();
|
||||||
CU_add_test(suite, "Trivial", test_trivial);
|
register_parser();
|
||||||
|
|
||||||
CU_basic_set_mode(CU_BRM_VERBOSE);
|
CU_basic_set_mode(CU_BRM_VERBOSE);
|
||||||
CU_basic_run_tests();
|
CU_basic_run_tests();
|
||||||
|
@ -19,6 +17,6 @@ int main()
|
||||||
|
|
||||||
int status = CU_get_number_of_failures();
|
int status = CU_get_number_of_failures();
|
||||||
CU_cleanup_registry();
|
CU_cleanup_registry();
|
||||||
|
errors_free();
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
#ifndef SK_TEST_PARSER_H
|
||||||
|
#define SK_TEST_PARSER_H
|
||||||
|
|
||||||
|
#include <CUnit/CUnit.h>
|
||||||
|
#include "node.h"
|
||||||
|
#include <parser.h>
|
||||||
|
|
||||||
|
static void test_parser(char const* oracle, char const* source)
|
||||||
|
{
|
||||||
|
struct parser parser;
|
||||||
|
parser_init(&parser, source);
|
||||||
|
struct node* node = parser_try_parse(&parser);
|
||||||
|
CU_ASSERT_FATAL(node != NULL);
|
||||||
|
|
||||||
|
struct str str;
|
||||||
|
str_init(&str);
|
||||||
|
node_str(node, &str);
|
||||||
|
CU_ASSERT_STRING_EQUAL(oracle, str.value);
|
||||||
|
str_free(&str);
|
||||||
|
|
||||||
|
node_free(node);
|
||||||
|
free(node);
|
||||||
|
parser_free(&parser);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_parser_int()
|
||||||
|
{
|
||||||
|
test_parser("ROOT(INT[32])", " 32 ");
|
||||||
|
test_parser("ROOT(INT[32],INT[-2],INT[24])", " 32 -2 24");
|
||||||
|
}
|
||||||
|
|
||||||
|
void register_parser()
|
||||||
|
{
|
||||||
|
CU_pSuite suite = CU_add_suite("Parser", 0, 0);
|
||||||
|
CU_add_test(suite, "Integers", test_parser_int);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in New Issue