ADD: booleans.
parent
768adc4826
commit
6592f2cfe5
|
@ -0,0 +1,4 @@
|
|||
*~*
|
||||
*\#*
|
||||
build
|
||||
doc/build
|
|
@ -0,0 +1,8 @@
|
|||
.PHONY: build install tests
|
||||
|
||||
build:
|
||||
meson setup build
|
||||
meson compile -C build
|
||||
|
||||
install: build
|
||||
meson install -C build
|
|
@ -0,0 +1,2 @@
|
|||
all:
|
||||
sphinx-build . build
|
|
@ -0,0 +1,2 @@
|
|||
project = 'WuZ'
|
||||
html_theme = 'press'
|
|
@ -0,0 +1,9 @@
|
|||
WuZ Programming Language
|
||||
========================
|
||||
|
||||
Table of Contents
|
||||
-----------------
|
||||
|
||||
.. toctree::
|
||||
installation
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
How to install WuZ
|
||||
==================
|
||||
|
||||
Let's install WuZ !
|
||||
For now, the only way is to compile it by yourself.
|
||||
|
||||
Compiling WuZ using Makefile (aka the easy way)
|
||||
-----------------------------------------------
|
||||
|
||||
You can compile the project using the Makefile
|
||||
at the repository root.
|
||||
Then you can install WuZ using the ``make install`` command.
|
||||
You may need to be root, if so, just do ``sudo make install`` instead.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
make
|
||||
make install
|
||||
|
||||
Et voila ! You are ready to use WuZ.
|
||||
|
||||
Compiling WuZ using Meson
|
||||
---------------------------
|
||||
If you prefere not to use the provided Makefile,
|
||||
you can use meson directly.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
meson setup build
|
||||
meson compile -C build
|
||||
meson install -C build
|
||||
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
project(
|
||||
'WuZ',
|
||||
'c',
|
||||
version: '0.0.0',
|
||||
default_options: [
|
||||
'warning_level=3'
|
||||
]
|
||||
)
|
||||
|
||||
flex = find_program('flex')
|
||||
flex_target = custom_target(
|
||||
'flex',
|
||||
input: ['src/lex.l'],
|
||||
output: ['lex.yy.c'],
|
||||
command: [
|
||||
flex, '@INPUT@'
|
||||
]
|
||||
)
|
||||
|
||||
bison = find_program('bison')
|
||||
bison_target = custom_target(
|
||||
'bison',
|
||||
input: ['src/parser.y'],
|
||||
output: ['parser.c'],
|
||||
command: [
|
||||
bison, '-d', '-o', '@OUTPUT@', '@INPUT@'
|
||||
]
|
||||
)
|
||||
executable(
|
||||
'wuz',
|
||||
sources: [
|
||||
flex_target,
|
||||
bison_target,
|
||||
'src/main.c',
|
||||
'src/node.c',
|
||||
'src/utils.c',
|
||||
'src/opcodes.c',
|
||||
'src/program.c',
|
||||
'src/value.c',
|
||||
'src/type.c',
|
||||
'src/compiler.c',
|
||||
'src/vm.c',
|
||||
],
|
||||
install: true
|
||||
)
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
#ifndef COMMONS_H
|
||||
#define COMMON_H
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "utils.h"
|
||||
#include "mutils.h"
|
||||
#endif
|
|
@ -0,0 +1,76 @@
|
|||
#include "compiler.h"
|
||||
|
||||
void compiler_init(compiler* self)
|
||||
{
|
||||
assert(self);
|
||||
}
|
||||
|
||||
void compiler_free(compiler* self)
|
||||
{
|
||||
assert(self);
|
||||
}
|
||||
|
||||
void compile_node(compiler* self, node* root, program* prog)
|
||||
{
|
||||
assert(self);
|
||||
assert(root);
|
||||
assert(prog);
|
||||
|
||||
if (root->type == NODE_BOOLEAN)
|
||||
{
|
||||
value val;
|
||||
value_init_boolean(&val,
|
||||
strcmp(root->value, "true") == 0,
|
||||
root->lineno);
|
||||
size_t idx = program_add_pool(prog, &val);
|
||||
program_add_instr(prog, OP_PUSH, idx);
|
||||
value_free(&val);
|
||||
}
|
||||
else if (root->type == NODE_ASSERT)
|
||||
{
|
||||
compile_children(self, root, prog);
|
||||
program_add_instr(prog, OP_ASSERT, NO_PARAM);
|
||||
}
|
||||
else if (root->type == NODE_EQ)
|
||||
{
|
||||
compile_children(self, root, prog);
|
||||
program_add_instr(prog, OP_EQ, NO_PARAM);
|
||||
}
|
||||
else if (root->type == NODE_NE)
|
||||
{
|
||||
compile_children(self, root, prog);
|
||||
program_add_instr(prog, OP_EQ, NO_PARAM);
|
||||
program_add_instr(prog, OP_NOT, NO_PARAM);
|
||||
}
|
||||
else if (root->type == NODE_AND)
|
||||
{
|
||||
compile_children(self, root, prog);
|
||||
program_add_instr(prog, OP_AND, NO_PARAM);
|
||||
}
|
||||
else if (root->type == NODE_OR)
|
||||
{
|
||||
compile_children(self, root, prog);
|
||||
program_add_instr(prog, OP_OR, NO_PARAM);
|
||||
}
|
||||
else if (root->type == NODE_NOT)
|
||||
{
|
||||
compile_children(self, root, prog);
|
||||
program_add_instr(prog, OP_NOT, NO_PARAM);
|
||||
}
|
||||
else
|
||||
{
|
||||
compile_children(self, root, prog);
|
||||
}
|
||||
}
|
||||
|
||||
void compile_children(compiler* self, node* root, program* prog)
|
||||
{
|
||||
assert(self);
|
||||
assert(root);
|
||||
assert(prog);
|
||||
|
||||
for (size_t i=0; i<root->children.size; i++)
|
||||
{
|
||||
compile_node(self, root->children.data[i], prog);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
#ifndef COMPILER_H
|
||||
#define COMPILER_H
|
||||
|
||||
#include "commons.h"
|
||||
#include "program.h"
|
||||
#include "node.h"
|
||||
|
||||
typedef struct {
|
||||
int _unused;
|
||||
} compiler;
|
||||
|
||||
void compiler_init(compiler* self);
|
||||
void compiler_free(compiler* self);
|
||||
|
||||
void compile_node(compiler* self, node* root, program* prog);
|
||||
void compile_children(compiler* self, node* root, program* prog);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,38 @@
|
|||
%{
|
||||
#include "parser.h"
|
||||
#include "src/utils.h"
|
||||
int line = 1;
|
||||
%}
|
||||
%option noyywrap
|
||||
%option nounput
|
||||
%option noinput
|
||||
|
||||
COMMENT ::[^\n]*
|
||||
WHITESPACES [ \t]+
|
||||
BOOLEAN true|false
|
||||
|
||||
%%
|
||||
"\n" { line++; }
|
||||
{COMMENT} {}
|
||||
{WHITESPACES} {}
|
||||
|
||||
"assert" { return ASSERT; }
|
||||
"==" { return EQ; }
|
||||
"!=" { return NE; }
|
||||
"&&" { return AND; }
|
||||
"||" { return OR; }
|
||||
"!" { return NOT; }
|
||||
"(" { return OPAR; }
|
||||
")" { return CPAR; }
|
||||
|
||||
{BOOLEAN} {
|
||||
yylval.boolean = yytext;
|
||||
return BOOLEAN;
|
||||
}
|
||||
. {
|
||||
fprintf(stderr, "E(%d): Lexical error near '%s'.\n", line, yytext);
|
||||
exit(-1);
|
||||
}
|
||||
%%
|
||||
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
#include <stdio.h>
|
||||
#include "node.h"
|
||||
#include "parser.h"
|
||||
#include "compiler.h"
|
||||
#include "vm.h"
|
||||
|
||||
extern FILE* yyin;
|
||||
extern node* ast;
|
||||
extern void stack_push(node*);
|
||||
extern node* stack_pop();
|
||||
extern void stack_free();
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
if (argc > 1)
|
||||
{
|
||||
yyin = fopen(argv[1], "r");
|
||||
|
||||
node* n = malloc(sizeof(node));
|
||||
node_init(n, NODE_PROG, "", 1);
|
||||
stack_push(n);
|
||||
|
||||
yyparse();
|
||||
|
||||
ast = stack_pop();
|
||||
stack_free();
|
||||
|
||||
compiler comp;
|
||||
compiler_init(&comp);
|
||||
|
||||
program prog;
|
||||
program_init(&prog);
|
||||
|
||||
compile_node(&comp, ast, &prog);
|
||||
|
||||
vm v;
|
||||
vm_init(&v);
|
||||
|
||||
vm_exec(&v, &prog);
|
||||
|
||||
if (0) // DEBUG
|
||||
{
|
||||
size_t const BUF = 1024;
|
||||
char buffer[BUF];
|
||||
|
||||
node_str(ast, buffer, BUF);
|
||||
printf("-- ast ---\n%s\n\n", buffer);
|
||||
|
||||
program_str(&prog, buffer, BUF);
|
||||
printf("--- program ---\n%s\n", buffer);
|
||||
|
||||
vm_str(&v, buffer, BUF);
|
||||
printf("--- stack ---\n%s\n", buffer);
|
||||
}
|
||||
|
||||
vm_free(&v);
|
||||
|
||||
program_free(&prog);
|
||||
compiler_free(&comp);
|
||||
node_free(ast);
|
||||
|
||||
fclose(yyin);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
fprintf(stderr, "Usage: wuz <filename>\n");
|
||||
return -1;
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
#ifndef MUTILS_H
|
||||
#define MUTILS_H
|
||||
|
||||
#define GEN_ENUM(X) X
|
||||
#define GEN_STRING(X) #X
|
||||
|
||||
#endif
|
|
@ -0,0 +1,96 @@
|
|||
#include "node.h"
|
||||
|
||||
|
||||
char const* NodeTypeStr[] = {
|
||||
NODE_TYPE(GEN_STRING)
|
||||
};
|
||||
|
||||
void node_init(node* self, int type, char const* value, int lineno)
|
||||
{
|
||||
assert(self);
|
||||
self->type = type;
|
||||
self->lineno = lineno;
|
||||
|
||||
self->value = str_new(value);
|
||||
self->children.data = NULL;
|
||||
self->children.size = 0;
|
||||
self->children.capacity = 0;
|
||||
}
|
||||
|
||||
void node_free(node* self)
|
||||
{
|
||||
assert(self);
|
||||
assert(self->value);
|
||||
free(self->value);
|
||||
self->value = NULL;
|
||||
|
||||
for (size_t i=0; i<self->children.size; i++)
|
||||
{
|
||||
node_free(self->children.data[i]);
|
||||
free(self->children.data[i]);
|
||||
}
|
||||
|
||||
free(self->children.data);
|
||||
self->children.data = NULL;
|
||||
self->children.size = 0;
|
||||
self->children.capacity = 0;
|
||||
}
|
||||
|
||||
|
||||
void node_add_child(node* self, node* child)
|
||||
{
|
||||
assert(self);
|
||||
assert(child);
|
||||
|
||||
if (self->children.capacity == 0)
|
||||
{
|
||||
self->children.capacity = 1;
|
||||
self->children.data = malloc(sizeof(node*)
|
||||
* self->children.capacity);
|
||||
}
|
||||
else if (self->children.size >= self->children.capacity)
|
||||
{
|
||||
self->children.capacity *= 2;
|
||||
self->children.data = realloc(self->children.data,
|
||||
sizeof(node*)
|
||||
* self->children.capacity);
|
||||
}
|
||||
|
||||
self->children.data[self->children.size] = child;
|
||||
self->children.size++;
|
||||
}
|
||||
|
||||
size_t node_str(node* self, char* buffer, size_t size)
|
||||
{
|
||||
assert(self);
|
||||
size_t sz = 0;
|
||||
|
||||
char const* ty = NodeTypeStr[self->type] + strlen("NODE_");
|
||||
|
||||
sz += snprintf(buffer + sz, size - sz, "%s", ty);
|
||||
|
||||
if (strcmp(self->value, "") != 0)
|
||||
{
|
||||
sz += snprintf(buffer + sz, size - sz, "[%s]", self->value);
|
||||
}
|
||||
|
||||
if (self->children.size > 0)
|
||||
{
|
||||
sz += snprintf(buffer + sz, size - sz, "(");
|
||||
for (size_t i=0; i<self->children.size; i++)
|
||||
{
|
||||
if (i > 0)
|
||||
{
|
||||
sz += snprintf(buffer + sz, size - sz, ",");
|
||||
}
|
||||
|
||||
sz += node_str(self->children.data[i],
|
||||
buffer + sz,
|
||||
size - sz);
|
||||
}
|
||||
|
||||
sz += snprintf(buffer + sz, size - sz, ")");
|
||||
}
|
||||
|
||||
return sz;
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
#ifndef NODE_H
|
||||
#define NODE_H
|
||||
|
||||
#define NODE_TYPE(G) \
|
||||
G(NODE_PROG), \
|
||||
G(NODE_BOOLEAN), \
|
||||
G(NODE_AND), G(NODE_OR), G(NODE_NOT), \
|
||||
G(NODE_EQ), G(NODE_NE), \
|
||||
G(NODE_ASSERT)
|
||||
|
||||
#include "mutils.h"
|
||||
#include "commons.h"
|
||||
|
||||
enum NodeType {
|
||||
NODE_TYPE(GEN_ENUM)
|
||||
};
|
||||
|
||||
extern char const* NodeTypeStr[];
|
||||
|
||||
typedef struct node {
|
||||
int type;
|
||||
char* value;
|
||||
struct {
|
||||
size_t capacity;
|
||||
size_t size;
|
||||
struct node** data;
|
||||
} children;
|
||||
|
||||
int lineno;
|
||||
} node;
|
||||
|
||||
void node_init(node* self, int type, char const* value, int lineno);
|
||||
void node_free(node* self);
|
||||
|
||||
void node_add_child(node* self, node* child);
|
||||
|
||||
size_t node_str(node* self, char* buffer, size_t size);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,5 @@
|
|||
#include "opcodes.h"
|
||||
|
||||
char const* OpcodesStr[] = {
|
||||
OPCODES(GEN_STRING)
|
||||
};
|
|
@ -0,0 +1,17 @@
|
|||
#ifndef OPCODES_H
|
||||
#define OPCODES_H
|
||||
|
||||
#include "mutils.h"
|
||||
|
||||
#define OPCODES(G) \
|
||||
G(OP_PUSH), \
|
||||
G(OP_AND), G(OP_OR), G(OP_NOT), \
|
||||
G(OP_EQ), G(OP_ASSERT)
|
||||
|
||||
enum Opcodes {
|
||||
OPCODES(GEN_ENUM)
|
||||
};
|
||||
|
||||
extern char const* OpcodesStr[];
|
||||
|
||||
#endif
|
|
@ -0,0 +1,163 @@
|
|||
%{
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "src/node.h"
|
||||
|
||||
extern int line;
|
||||
void yyerror(char const*);
|
||||
node* ast = NULL;
|
||||
|
||||
node** stack = NULL;
|
||||
size_t stack_sz = 0;
|
||||
size_t stack_cap = 0;
|
||||
void stack_push(node* n);
|
||||
node* stack_pop();
|
||||
node* stack_top();
|
||||
void stack_free();
|
||||
%}
|
||||
|
||||
%union {
|
||||
char* boolean;
|
||||
void* node_val;
|
||||
};
|
||||
|
||||
%left ASSERT
|
||||
%token <boolean> BOOLEAN
|
||||
%left EQ NE
|
||||
%left AND
|
||||
%left OR
|
||||
%left NOT
|
||||
%type <node_val> expr;
|
||||
%token OPAR CPAR
|
||||
|
||||
%%
|
||||
|
||||
prog: exprs {
|
||||
|
||||
}
|
||||
;
|
||||
|
||||
exprs:
|
||||
exprs expr {
|
||||
node* parent = stack_top();
|
||||
node_add_child(parent, $2);
|
||||
}
|
||||
|
||||
| expr {
|
||||
node* parent = stack_top();
|
||||
node_add_child(parent, $1);
|
||||
}
|
||||
;
|
||||
|
||||
|
||||
expr:
|
||||
ASSERT expr {
|
||||
node *n = malloc(sizeof(node));
|
||||
node_init(n, NODE_ASSERT, "", line);
|
||||
node_add_child(n, $2);
|
||||
$$ = n;
|
||||
}
|
||||
|
||||
|
||||
| expr EQ expr {
|
||||
node *n = malloc(sizeof(node));
|
||||
node_init(n, NODE_EQ, "", line);
|
||||
node_add_child(n, $1);
|
||||
node_add_child(n, $3);
|
||||
$$ = n;
|
||||
}
|
||||
|
||||
| expr NE expr {
|
||||
node *n = malloc(sizeof(node));
|
||||
node_init(n, NODE_NE, "", line);
|
||||
node_add_child(n, $1);
|
||||
node_add_child(n, $3);
|
||||
$$ = n;
|
||||
}
|
||||
|
||||
| expr AND expr {
|
||||
node *n = malloc(sizeof(node));
|
||||
node_init(n, NODE_AND, "", line);
|
||||
node_add_child(n, $1);
|
||||
node_add_child(n, $3);
|
||||
$$ = n;
|
||||
}
|
||||
|
||||
| expr OR expr {
|
||||
node *n = malloc(sizeof(node));
|
||||
node_init(n, NODE_OR, "", line);
|
||||
node_add_child(n, $1);
|
||||
node_add_child(n, $3);
|
||||
$$ = n;
|
||||
}
|
||||
|
||||
| NOT expr {
|
||||
node *n = malloc(sizeof(node));
|
||||
node_init(n, NODE_NOT, "", line);
|
||||
node_add_child(n, $2);
|
||||
$$ = n;
|
||||
}
|
||||
|
||||
| OPAR expr CPAR {
|
||||
$$ = $2;
|
||||
}
|
||||
|
||||
|
||||
| BOOLEAN {
|
||||
node* n = malloc(sizeof(node));
|
||||
node_init(n, NODE_BOOLEAN, $1, line);
|
||||
$$ = n;
|
||||
}
|
||||
;
|
||||
|
||||
%%
|
||||
|
||||
void yyerror(char const* msg)
|
||||
{
|
||||
fprintf(stderr, "E(%d): %s\n", line, msg);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
void stack_push(node* n)
|
||||
{
|
||||
if (stack_cap == 0)
|
||||
{
|
||||
stack = malloc(sizeof(node*));
|
||||
stack_cap = 1;
|
||||
}
|
||||
else if (stack_sz >= stack_cap)
|
||||
{
|
||||
stack_cap *= 2;
|
||||
stack = realloc(stack, sizeof(node*) * stack_cap);
|
||||
}
|
||||
|
||||
stack[stack_sz] = n;
|
||||
stack_sz++;
|
||||
}
|
||||
|
||||
node* stack_pop()
|
||||
{
|
||||
assert(stack_sz > 0);
|
||||
node* n = stack[stack_sz - 1];
|
||||
stack_sz--;
|
||||
return n;
|
||||
}
|
||||
|
||||
node* stack_top()
|
||||
{
|
||||
assert(stack_sz > 0);
|
||||
node* n = stack[stack_sz - 1];
|
||||
return n;
|
||||
}
|
||||
|
||||
void stack_free()
|
||||
{
|
||||
for (size_t i=0; i<stack_sz; i++)
|
||||
{
|
||||
node_free(stack[i]);
|
||||
free(stack[i]);
|
||||
}
|
||||
|
||||
free(stack);
|
||||
stack = NULL;
|
||||
}
|
|
@ -0,0 +1,133 @@
|
|||
#include "program.h"
|
||||
|
||||
void program_init(program* self)
|
||||
{
|
||||
assert(self);
|
||||
self->instrs.size = 0;
|
||||
self->instrs.capacity = 0;
|
||||
self->instrs.data = NULL;
|
||||
self->pool.size = 0;
|
||||
self->pool.capacity = 0;
|
||||
self->pool.data = NULL;
|
||||
}
|
||||
|
||||
void program_free(program* self)
|
||||
{
|
||||
assert(self);
|
||||
|
||||
for (size_t i=0; i<self->instrs.size; i++)
|
||||
{
|
||||
free(self->instrs.data[i]);
|
||||
}
|
||||
|
||||
free(self->instrs.data);
|
||||
self->instrs.data = NULL;
|
||||
self->instrs.size = 0;
|
||||
self->instrs.capacity = 0;
|
||||
|
||||
for (size_t i=0; i<self->pool.size; i++)
|
||||
{
|
||||
value_free(self->pool.data[i]);
|
||||
free(self->pool.data[i]);
|
||||
}
|
||||
|
||||
free(self->pool.data);
|
||||
self->pool.data = NULL;
|
||||
self->pool.size = 0;
|
||||
self->pool.capacity = 0;
|
||||
}
|
||||
|
||||
void program_add_instr(program* self, int opcode, int param)
|
||||
{
|
||||
assert(self);
|
||||
|
||||
if (self->instrs.capacity == 0)
|
||||
{
|
||||
self->instrs.capacity = 1;
|
||||
self->instrs.data = malloc(sizeof(instr*)
|
||||
* self->instrs.capacity);
|
||||
}
|
||||
else if (self->instrs.size >= self->instrs.capacity)
|
||||
{
|
||||
self->instrs.capacity *= 2;
|
||||
self->instrs.data = realloc(
|
||||
self->instrs.data,
|
||||
sizeof(instr) * self->instrs.capacity
|
||||
);
|
||||
}
|
||||
size_t idx = self->instrs.size;
|
||||
|
||||
self->instrs.data[idx] = malloc(sizeof(instr));
|
||||
self->instrs.data[idx]->opcode = opcode;
|
||||
self->instrs.data[idx]->param = param;
|
||||
|
||||
self->instrs.size++;
|
||||
}
|
||||
|
||||
size_t program_add_pool(program* self, value* val)
|
||||
{
|
||||
assert(self);
|
||||
assert(val);
|
||||
|
||||
if (self->pool.capacity == 0)
|
||||
{
|
||||
self->pool.capacity = 1;
|
||||
self->pool.data = malloc(sizeof(value*)
|
||||
* self->pool.capacity);
|
||||
}
|
||||
else if (self->pool.size >= self->pool.capacity)
|
||||
{
|
||||
self->pool.capacity *= 2;
|
||||
self->pool.data = realloc(
|
||||
self->pool.data,
|
||||
sizeof(value) * self->pool.capacity
|
||||
);
|
||||
}
|
||||
size_t idx = self->pool.size;
|
||||
self->pool.data[idx] = value_new_clone(val);
|
||||
|
||||
self->pool.size++;
|
||||
|
||||
return idx;
|
||||
}
|
||||
|
||||
size_t program_str(program* self, char* buffer, size_t size)
|
||||
{
|
||||
assert(self);
|
||||
size_t sz = 0;
|
||||
|
||||
for (size_t i=0; i<self->instrs.size; i++)
|
||||
{
|
||||
int opcode = self->instrs.data[i]->opcode;
|
||||
int param = self->instrs.data[i]->param;
|
||||
|
||||
if (param != NO_PARAM)
|
||||
{
|
||||
sz += snprintf(buffer + sz, size - sz, "%ld\t%s\t%d",
|
||||
i,
|
||||
OpcodesStr[opcode]
|
||||
+ strlen("OP_"),
|
||||
param);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
sz += snprintf(buffer + sz, size - sz, "%ld\t%s\t",
|
||||
i,
|
||||
OpcodesStr[opcode]
|
||||
+ strlen("OP_"));
|
||||
}
|
||||
|
||||
if (opcode == OP_PUSH && param != -1)
|
||||
{
|
||||
sz += snprintf(buffer + sz, size - sz, "\t (");
|
||||
sz += value_str(self->pool.data[param],
|
||||
buffer + sz, size - sz);
|
||||
sz += snprintf(buffer + sz, size - sz, ")");
|
||||
}
|
||||
|
||||
sz += snprintf(buffer + sz, size - sz, "\n");
|
||||
}
|
||||
|
||||
return sz;
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
#ifndef PROGRAM_H
|
||||
#define PROGRAM_H
|
||||
|
||||
#include "commons.h"
|
||||
#include "opcodes.h"
|
||||
#include "value.h"
|
||||
|
||||
#define NO_PARAM (-1)
|
||||
|
||||
typedef struct {
|
||||
int opcode;
|
||||
int param;
|
||||
} instr;
|
||||
|
||||
typedef struct {
|
||||
struct {
|
||||
size_t capacity;
|
||||
size_t size;
|
||||
instr** data;
|
||||
} instrs;
|
||||
|
||||
struct {
|
||||
size_t capacity;
|
||||
size_t size;
|
||||
value** data;
|
||||
} pool;
|
||||
} program;
|
||||
|
||||
void program_init(program* self);
|
||||
void program_free(program* self);
|
||||
|
||||
void program_add_instr(program* self, int opcode, int param);
|
||||
size_t program_add_pool(program* self, value* val);
|
||||
|
||||
size_t program_str(program* self, char* buffer, size_t size);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,39 @@
|
|||
#include "type.h"
|
||||
|
||||
|
||||
char const* TypesStr[] = { TYPES(GEN_STRING) };
|
||||
|
||||
void type_init(type* self, int base_type)
|
||||
{
|
||||
assert(self);
|
||||
self->base_type = base_type;
|
||||
}
|
||||
|
||||
void type_free(type* self)
|
||||
{
|
||||
assert(self);
|
||||
}
|
||||
|
||||
type* type_new_clone(type* self)
|
||||
{
|
||||
assert(self);
|
||||
|
||||
type* clone = malloc(sizeof(type));
|
||||
type_init(clone, self->base_type);
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
int type_equals(type* self, type* rhs)
|
||||
{
|
||||
assert(self);
|
||||
assert(rhs);
|
||||
|
||||
return self->base_type == rhs->base_type;
|
||||
}
|
||||
|
||||
size_t type_str(type* self, char* buffer, size_t size)
|
||||
{
|
||||
assert(self);
|
||||
return snprintf(buffer, size, "%s", TypesStr[self->base_type]);
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
#ifndef TYPE_H
|
||||
#define TYPE
|
||||
|
||||
#include "commons.h"
|
||||
|
||||
#define TYPES(G) \
|
||||
G(TY_BOOLEAN)
|
||||
|
||||
enum Types {
|
||||
TYPES(GEN_ENUM)
|
||||
};
|
||||
|
||||
extern char const* TypesStr[];
|
||||
|
||||
typedef struct type {
|
||||
int base_type;
|
||||
} type;
|
||||
|
||||
void type_init(type* self, int base_type);
|
||||
void type_free(type* self);
|
||||
|
||||
type* type_new_clone(type* self);
|
||||
int type_equals(type* self, type* rhs);
|
||||
size_t type_str(type* self, char* buffer, size_t size);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,12 @@
|
|||
#include "utils.h"
|
||||
|
||||
char const* str_new(char const* str)
|
||||
{
|
||||
size_t len = strlen(str) + 1;
|
||||
|
||||
char* result = malloc(len);
|
||||
|
||||
memcpy(result, str, len);
|
||||
|
||||
return result;
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
#ifndef UTILS_H
|
||||
#define UTILS_H
|
||||
#include <string.h>
|
||||
|
||||
char const* str_new(char const* str);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,73 @@
|
|||
#include "value.h"
|
||||
|
||||
void value_init_boolean(value* self, int boolean, int lineno)
|
||||
{
|
||||
assert(self);
|
||||
self->val.boolean = boolean;
|
||||
self->lineno = lineno;
|
||||
|
||||
self->type = malloc(sizeof(type));
|
||||
type_init(self->type, TY_BOOLEAN);
|
||||
}
|
||||
|
||||
void value_free(value* self)
|
||||
{
|
||||
assert(self);
|
||||
type_free(self->type);
|
||||
free(self->type);
|
||||
self->type = NULL;
|
||||
}
|
||||
|
||||
value* value_new_clone(value* self)
|
||||
{
|
||||
assert(self);
|
||||
value* clone = malloc(sizeof(value));
|
||||
clone->type = type_new_clone(self->type);
|
||||
clone->val = self->val;
|
||||
clone->lineno = self->lineno;
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
|
||||
int value_equals(value* self, value* rhs)
|
||||
{
|
||||
assert(self);
|
||||
assert(rhs);
|
||||
|
||||
if (!type_equals(self->type, rhs->type))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (self->type->base_type == TY_BOOLEAN)
|
||||
{
|
||||
return self->val.boolean == rhs->val.boolean;
|
||||
}
|
||||
|
||||
size_t const SZ = 512;
|
||||
char ty_str[SZ];
|
||||
|
||||
type_str(self->type, ty_str, SZ);
|
||||
|
||||
fprintf(stderr, "E: cannot test value equality: unknown type '%s'\n",
|
||||
ty_str);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
size_t value_str(value* self, char* buffer, size_t size)
|
||||
{
|
||||
assert(self);
|
||||
|
||||
switch (self->type->base_type)
|
||||
{
|
||||
case TY_BOOLEAN:
|
||||
return snprintf(buffer, size, "%s",
|
||||
self->val.boolean == 0
|
||||
? "false" : "true");
|
||||
default: {
|
||||
fprintf(stderr, "E: unknown value");
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
#ifndef VALUE_H
|
||||
#define VALUE_H
|
||||
|
||||
#include "commons.h"
|
||||
#include "type.h"
|
||||
|
||||
typedef struct {
|
||||
type* type;
|
||||
int lineno;
|
||||
union {
|
||||
int boolean;
|
||||
} val;
|
||||
} value;
|
||||
|
||||
void value_init_boolean(value* self, int boolean, int lineno);
|
||||
void value_free(value* self);
|
||||
|
||||
value* value_new_clone(value* self);
|
||||
int value_equals(value* self, value* rhs);
|
||||
|
||||
size_t value_str(value* self, char* buffer, size_t size);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,200 @@
|
|||
#include "vm.h"
|
||||
|
||||
void vm_init(vm* self)
|
||||
{
|
||||
assert(self);
|
||||
|
||||
self->stack.size = 0;
|
||||
self->stack.capacity = 1;
|
||||
self->stack.data = malloc(sizeof(value*) * self->stack.capacity);
|
||||
|
||||
self->prog = NULL;
|
||||
self->pc = 0;
|
||||
}
|
||||
|
||||
void vm_free(vm* self)
|
||||
{
|
||||
assert(self);
|
||||
for (size_t i=0; i<self->stack.size; i++)
|
||||
{
|
||||
value_free(self->stack.data[i]);
|
||||
free(self->stack.data[i]);
|
||||
}
|
||||
|
||||
free(self->stack.data);
|
||||
|
||||
self->stack.size = 0;
|
||||
self->stack.capacity = 0;
|
||||
self->stack.data = NULL;
|
||||
}
|
||||
|
||||
void vm_exec(vm* self, program* prog)
|
||||
{
|
||||
assert(self);
|
||||
assert(prog);
|
||||
self->prog = prog;
|
||||
|
||||
while (self->pc < prog->instrs.size)
|
||||
{
|
||||
int opcode = prog->instrs.data[self->pc]->opcode;
|
||||
int param = prog->instrs.data[self->pc]->param;
|
||||
|
||||
switch (opcode)
|
||||
{
|
||||
case OP_PUSH: vm_push(self, param); break;
|
||||
case OP_AND: vm_and(self); break;
|
||||
case OP_OR: vm_or(self); break;
|
||||
case OP_NOT: vm_not(self); break;
|
||||
case OP_EQ: vm_eq(self); break;
|
||||
case OP_ASSERT: vm_assert(self); break;
|
||||
default: {
|
||||
fprintf(stderr, "unknown opcode %s\n",
|
||||
OpcodesStr[opcode]);
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void vm_push_value(vm* self, value* val)
|
||||
{
|
||||
assert(self);
|
||||
|
||||
if (self->stack.size >= self->stack.capacity)
|
||||
{
|
||||
self->stack.capacity *= 2;
|
||||
self->stack.data = realloc(
|
||||
self->stack.data,
|
||||
sizeof(value*) * self->stack.capacity
|
||||
);
|
||||
}
|
||||
|
||||
self->stack.data[self->stack.size] = val;
|
||||
self->stack.size++;
|
||||
}
|
||||
|
||||
value* vm_pop_value(vm* self)
|
||||
{
|
||||
assert(self);
|
||||
assert(self->stack.size > 0);
|
||||
|
||||
value* val = self->stack.data[self->stack.size - 1];
|
||||
|
||||
self->stack.size--;
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
size_t vm_str(vm* self, char* buffer, size_t size)
|
||||
{
|
||||
assert(self);
|
||||
size_t sz = 0;
|
||||
|
||||
for (size_t i=0; i<self->stack.size; i++)
|
||||
{
|
||||
sz += snprintf(buffer + sz, size - sz,
|
||||
"%ld\t", i);
|
||||
|
||||
|
||||
sz += value_str(self->stack.data[i], buffer + sz, size - sz);
|
||||
|
||||
sz += snprintf(buffer + sz, size - sz,
|
||||
"\n");
|
||||
}
|
||||
|
||||
return sz;
|
||||
}
|
||||
|
||||
void vm_push(vm* self, int param)
|
||||
{
|
||||
assert(self);
|
||||
assert(param >= 0);
|
||||
|
||||
value* val = self->prog->pool.data[param];
|
||||
vm_push_value(self, value_new_clone(val));
|
||||
|
||||
self->pc++;
|
||||
}
|
||||
|
||||
void vm_and(vm* self)
|
||||
{
|
||||
value* rhs = vm_pop_value(self);
|
||||
value* lhs = vm_pop_value(self);
|
||||
|
||||
value* val = malloc(sizeof(value));
|
||||
value_init_boolean(val, lhs->val.boolean && rhs->val.boolean, lhs->lineno);
|
||||
vm_push_value(self, val);
|
||||
|
||||
value_free(rhs);
|
||||
free(rhs);
|
||||
value_free(lhs);
|
||||
free(lhs);
|
||||
|
||||
self->pc++;
|
||||
}
|
||||
|
||||
void vm_or(vm* self)
|
||||
{
|
||||
value* rhs = vm_pop_value(self);
|
||||
value* lhs = vm_pop_value(self);
|
||||
|
||||
value* val = malloc(sizeof(value));
|
||||
value_init_boolean(val, lhs->val.boolean || rhs->val.boolean, lhs->lineno);
|
||||
vm_push_value(self, val);
|
||||
|
||||
value_free(rhs);
|
||||
free(rhs);
|
||||
value_free(lhs);
|
||||
free(lhs);
|
||||
|
||||
self->pc++;
|
||||
}
|
||||
|
||||
void vm_not(vm* self)
|
||||
{
|
||||
value* lhs = vm_pop_value(self);
|
||||
|
||||
value* val = malloc(sizeof(value));
|
||||
value_init_boolean(val, !lhs->val.boolean, lhs->lineno);
|
||||
vm_push_value(self, val);
|
||||
|
||||
value_free(lhs);
|
||||
free(lhs);
|
||||
|
||||
self->pc++;
|
||||
}
|
||||
|
||||
void vm_eq(vm* self)
|
||||
{
|
||||
assert(self);
|
||||
value* rhs = vm_pop_value(self);
|
||||
value* lhs = vm_pop_value(self);
|
||||
|
||||
value* val = malloc(sizeof(value));
|
||||
value_init_boolean(val, value_equals(lhs, rhs), lhs->lineno);
|
||||
|
||||
vm_push_value(self, val);
|
||||
|
||||
value_free(lhs); free(lhs);
|
||||
value_free(rhs); free(rhs);
|
||||
|
||||
self->pc++;
|
||||
}
|
||||
|
||||
void vm_assert(vm* self)
|
||||
{
|
||||
assert(self);
|
||||
value* lhs = vm_pop_value(self);
|
||||
|
||||
if (!lhs->val.boolean)
|
||||
{
|
||||
fprintf(stderr, "E(%d): assertion failed\n", lhs->lineno);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
value_free(lhs); free(lhs);
|
||||
|
||||
self->pc++;
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
#ifndef VM_H
|
||||
#define VM_H
|
||||
|
||||
#include "commons.h"
|
||||
#include "value.h"
|
||||
#include "program.h"
|
||||
|
||||
typedef struct {
|
||||
struct {
|
||||
size_t capacity;
|
||||
size_t size;
|
||||
value** data;
|
||||
} stack;
|
||||
|
||||
size_t pc;
|
||||
program* prog;
|
||||
} vm;
|
||||
|
||||
void vm_init(vm* self);
|
||||
void vm_free(vm* self);
|
||||
|
||||
void vm_exec(vm* self, program* prog);
|
||||
void vm_push_value(vm* self, value* val);
|
||||
value* vm_pop_value(vm* self);
|
||||
size_t vm_str(vm* self, char* buffer, size_t size);
|
||||
|
||||
void vm_push(vm* self, int param);
|
||||
void vm_and(vm* self);
|
||||
void vm_or(vm* self);
|
||||
void vm_not(vm* self);
|
||||
|
||||
void vm_eq(vm* self);
|
||||
void vm_assert(vm* self);
|
||||
|
||||
|
||||
|
||||
#endif
|
|
@ -0,0 +1,39 @@
|
|||
:: not operator
|
||||
assert true
|
||||
assert !false
|
||||
assert !!true
|
||||
|
||||
:: equal operator
|
||||
:: ==============
|
||||
|
||||
:: and operator
|
||||
assert true == true && true
|
||||
assert false == true && false
|
||||
assert false == false && true
|
||||
assert false == false && false
|
||||
|
||||
:: or operator
|
||||
assert true == true || true
|
||||
assert true == true || false
|
||||
assert true == false || true
|
||||
assert false == false || false
|
||||
|
||||
:: not equal operator
|
||||
:: ==================
|
||||
|
||||
:: and operator
|
||||
assert false != true && true
|
||||
assert true != true && false
|
||||
assert true != false && true
|
||||
assert true != false && false
|
||||
|
||||
:: or operator
|
||||
assert false != true || true
|
||||
assert false != true || false
|
||||
assert false != false || true
|
||||
assert true != false || false
|
||||
|
||||
:: groups
|
||||
assert !(true && false) == false || true
|
||||
assert !(true || false) == false && true
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
#!/bin/sh
|
||||
|
||||
OK=0
|
||||
KO=0
|
||||
|
||||
for file in $(find . -name "*.wuz")
|
||||
do
|
||||
wuz $file
|
||||
RES="$?"
|
||||
|
||||
echo -n "$file ... "
|
||||
|
||||
if [ "$RES" == "0" ]
|
||||
then
|
||||
echo -e "\e[32mok\e[0m"
|
||||
OK=$(($OK + 1))
|
||||
else
|
||||
echo -e "\e[31mko\e[0m"
|
||||
KO=$(($KO+1))
|
||||
fi
|
||||
done
|
||||
|
||||
TOTAL=$(($OK+$KO))
|
||||
|
||||
if [ $KO -eq 0 ]
|
||||
then
|
||||
echo -e "\e[32mAll tests passed [$TOTAL]\e[0m"
|
||||
else
|
||||
echo -e "\e[31m$KO tests failed [$TOTAL]\e[0m"
|
||||
fi
|
||||
|
Loading…
Reference in New Issue