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