first commit

This commit is contained in:
cozis
2021-10-31 04:17:57 +00:00
commit 7a5d0d1dcb
55 changed files with 5204 additions and 0 deletions
+217
View File
@@ -0,0 +1,217 @@
#ifndef XJSON_H
#define XJSON_H
#include <stdarg.h>
#include <stdio.h>
enum {
xj_INT = (1 << 0),
xj_BOOL = (1 << 1),
xj_NULL = (1 << 2),
xj_FLOAT = (1 << 3),
xj_ARRAY = (1 << 4),
xj_OBJECT = (1 << 5),
xj_STRING = (1 << 6),
xj_BIG_INT = (1 << 7),
xj_BIG_FLOAT = (1 << 8),
};
typedef enum {
xj_E0000 = 0, // No error.
// --------------------------- //
// --- Internal errors ------- //
// --------------------------- //
xj_E0001, // Maximum depth reached.
xj_E0002, // Out of memory.
xj_E0003, // Not implemented.
// --------------------------- //
// --- Input errors ---------- //
// --------------------------- //
// (Unexpexted end of source inside object) //
xj_E1000, // End of source inside object,
// right after the starting {
xj_E1001, // End of source inside object,
// right after a key string. Was
// expected :
xj_E1002, // End of source inside object,
// right after a key-value separator :.
// Was expected a value expression
xj_E1003, // End of source inside object,
// right after value expression.
// Were expected , or }
xj_E1004, // End of source inside object,
// right after value-key separator ,.
// Was expected a key string
// (Unexpected end of source inside array) //
xj_E1100, // End of source inside array,
// right after the starting [
xj_E1101, // End of source inside array,
// right after a value expression
xj_E1102, // End of source inside array,
// right after value separator ,.
// Was expected a new value
// expression
// (Unexpected end of source inside number) //
xj_E1200, // End of source inside numeric
// expression, right after minus
// sign. Was expected a digit
// (Unexpected end of source inside string) //
xj_E1300, // End of source inside string expression
xj_E1301, // End of source inside string expression, right after a backslash
// (Unexpected character inside object) //
xj_E2000, // Unexpected character in place of an object key string. Was expected "
xj_E2001, // Unexpected character in place of an object key-value separator. Was expected :
xj_E2002, // Unexpected character inside object, right after value expression. Were expected , or }
// (Unexpected character inside array) //
xj_E2100, // Unexpected character inside array, right after value expression. Were expecter ] or ,
// (Unexpected character inside number) //
xj_E2200, // Unexpected character inside number expression, right after minus sign. Was expected a digit after it
// (Unexpected character inside string) //
xj_E2300, // Unexpected character inside string expression, right after backslash. The only escapable characters are \, /, ", n, t, r, f, b and u.
xj_E2301, // Unexpected character inside string expression, after \u. Was expected a hex digit
xj_E2302, // Unexpected character inside string expression, right after unicode codepoint. Was expected an auxiliary codepoint
xj_E2303, // Unexpected character inside string expression, right after \\. Was expected an auxiliary codepoint
xj_E2304, // Invalid symbol
xj_E2305, // Invalid auxiliary symbol
xj_E2306, // Invalid codepoint
// (Unexpected end of source inside value expression) //
xj_E2400, // Unexpected character in place of a value expression. Were expected {, [, ", n, t, f or a digit.
// (Other) //
xj_E2500, // Unexpected character after root value expression
xj_E2501, // Source string is empty (null pointer)
xj_E2502, // Source string is empty
xj_E2503, // Duplicate key in object
// Decodef errors
xj_E3000, // Bad format
// Query errors
xj_E4000, // End of source after dot.
xj_E4001, // Invalid character after dot.
xj_E4002, // End of source after [.
xj_E4003, // Float used as index.
xj_E4004, // Index too big.
xj_E4005, // Invalid character after [.
xj_E4006, // End of source instead of closing ].
xj_E4007, // Invalid character instead of closing ].
xj_E4008, // Invalid character instead of dot and [.
xj_E4009, // Missing node.
// (i|f|b|s|a|o)Query errors
xj_E4100, // Queried int is too big so it's not representable.
xj_E4101, // Queried value is not an int.
xj_E4102, // Queried float is too big, so it's not representable.
xj_E4103, // Queried value is not a float.
xj_E4104, // Queried value is not a string.
xj_E4105, // Queried value is not a bool.
xj_E4106, // Queried value is not an array.
xj_E4107, // Queried value is not an object.
xj_ECOUNT,
} xj_code;
typedef struct {
const char *src;
_Bool occurred;
xj_code code;
char text[128];
int offset,
lineno;
const char *cfile,
*cfunc;
int cline;
} xj_error;
typedef void xj_alloc;
typedef struct xj_value xj_value;
struct xj_value {
int offset, length, type, size;
char *key;
union {
long long as_int;
_Bool as_bool;
double as_float;
xj_value* as_array;
xj_value* as_object;
char* as_string;
};
xj_value *next;
};
xj_alloc* xj_init (unsigned int default_chunk_size);
void xj_free (xj_alloc *alloc);
void* xj_malloc(xj_alloc *alloc, unsigned int size);
void xj_set_malloc(void *(*malloc)(void*, unsigned int));
void* xj_sys_malloc(unsigned int size);
void xj_set_free(void (*free)(void*, void*));
void xj_sys_free(void *ptr);
void xj_set_userp(void *userp);
int xj_snprintf (char *buff, int buffsz, const char *fmt, ...);
int xj_vsnprintf(char *buff, int buffsz, const char *fmt, va_list args);
xj_value* xj_query (xj_error *error, xj_value *root, const char *text, int len);
_Bool xj_nquery(xj_error *error, xj_value *root, const char *text, int len);
int xj_iquery(xj_error *error, xj_value *root, const char *text, int len);
double xj_fquery(xj_error *error, xj_value *root, const char *text, int len);
_Bool xj_bquery(xj_error *error, xj_value *root, const char *text, int len);
char *xj_squery(xj_error *error, xj_value *root, const char *text, int len, int *count);
xj_value *xj_aquery(xj_error *error, xj_value *root, const char *text, int len, int *count);
xj_value *xj_oquery(xj_error *error, xj_value *root, const char *text, int len, int *count);
xj_value* xj_decode (xj_error *error, xj_alloc **alloc, const char *text, int len);
xj_value* xj_decodef (xj_error *error, xj_alloc **alloc, const char *format, ...);
xj_value* xj_vdecodef(xj_error *error, xj_alloc **alloc, const char *format, va_list args);
char* xj_encode (const xj_value *value, int *len);
char* xj_sencode(const char *src, int *len);
char* xj_aencode(const xj_value *head, int *len);
char* xj_oencode(const xj_value *head, int *len);
char* xj_encode2 (const xj_value *value, char *buff, unsigned int bufflen, int *len);
char* xj_sencode2(const char *src, char *buff, unsigned int bufflen, int *len);
char* xj_aencode2(const xj_value *head, char *buff, unsigned int bufflen, int *len);
char* xj_oencode2(const xj_value *head, char *buff, unsigned int bufflen, int *len);
_Bool xj_compare(const xj_value *a, const xj_value *b);
xj_value* xj_get_by_name (const xj_value *parent, const char *name, int len);
xj_value* xj_get_by_index(const xj_value *parent, unsigned int index);
_Bool xj_is_error_internal(const xj_error *error);
const char *xj_typename(int type);
void xj_stats(FILE *fp);
#endif
Binary file not shown.
Executable
+80
View File
@@ -0,0 +1,80 @@
USING_VALGRIND=0
FLAGS="-L3p/libs/ -I3p/include/ -Wall -Wextra -g -DUSING_VALGRIND=$USING_VALGRIND -DTRACE_SOURCE_COPIES=$TRACE_SOURCE_COPIES"
mkdir temp
mkdir temp/utils
gcc -c src/utils/hash.c -o temp/utils/hash.o $FLAGS
gcc -c src/utils/stack.c -o temp/utils/stack.o $FLAGS
gcc -c src/utils/error.c -o temp/utils/error.o $FLAGS
gcc -c src/utils/source.c -o temp/utils/source.o $FLAGS
gcc -c src/utils/bpalloc.c -o temp/utils/bpalloc.o $FLAGS
gcc -c src/utils/promise.c -o temp/utils/promise.o $FLAGS
gcc -c src/utils/bucketlist.c -o temp/utils/bucketlist.o $FLAGS
mkdir temp/objects
gcc -c src/objects/heap.c -o temp/objects/heap.o $FLAGS
gcc -c src/objects/o_int.c -o temp/objects/o_int.o $FLAGS
gcc -c src/objects/o_map.c -o temp/objects/o_map.o $FLAGS
gcc -c src/objects/o_none.c -o temp/objects/o_none.o $FLAGS
gcc -c src/objects/o_float.c -o temp/objects/o_float.o $FLAGS
gcc -c src/objects/o_string.c -o temp/objects/o_string.o $FLAGS
gcc -c src/objects/objects.c -o temp/objects/objects.o $FLAGS
mkdir temp/compiler
gcc -c src/compiler/parse.c -o temp/compiler/parse.o $FLAGS
gcc -c src/compiler/compile.c -o temp/compiler/compile.o $FLAGS
gcc -c src/compiler/serialize.c -o temp/compiler/serialize.o $FLAGS
mkdir temp/common
gcc -c src/common/executable.c -o temp/common/executable.o $FLAGS
mkdir temp/runtime
gcc -c src/runtime/runtime_error.c -o temp/runtime/runtime_error.o $FLAGS
gcc -c src/runtime/runtime.c -o temp/runtime/runtime.o $FLAGS
gcc -c src/runtime/o_func.c -o temp/runtime/o_func.o $FLAGS
rm -rf build
mkdir build
ar rcs build/libnoja-objects.a \
temp/objects/heap.o \
temp/objects/o_int.o \
temp/objects/o_float.o \
temp/objects/objects.o \
temp/utils/error.o
ar rcs build/libnoja-compile.a \
temp/compiler/parse.o \
temp/compiler/compile.o \
temp/compiler/serialize.o \
temp/utils/bpalloc.o \
temp/utils/error.o \
temp/utils/source.o
ar rcs build/libnoja-runtime.a \
temp/runtime/runtime.o \
temp/runtime/runtime_error.o \
temp/runtime/o_func.o \
build/libnoja-compile.a \
build/libnoja-objects.a
gcc tests/src/test-parse.c -o build/test-parse $FLAGS -Lbuild/ -lnoja-compile -lxjson
gcc tests/src/test-objects.c -o build/test-objects $FLAGS -Lbuild/ -lnoja-objects
gcc src/main.c \
temp/utils/hash.o \
temp/utils/stack.o \
temp/utils/source.o \
temp/utils/promise.o \
temp/utils/bucketlist.o \
temp/objects/o_map.o \
temp/objects/o_none.o \
temp/objects/o_string.o \
temp/runtime/runtime.o \
temp/runtime/runtime_error.o \
temp/common/executable.o \
-o build/noja $FLAGS -Lbuild/ -lnoja-compile -lnoja-objects -lxjson
rm -rf temp
Binary file not shown.
Binary file not shown.
Binary file not shown.
Executable
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
View File
View File
+36
View File
@@ -0,0 +1,36 @@
stack = {count: 0, head: none};
fun push(stack, value) {
node = {prev: null, item: value};
node.prev = list.head;
stack.head = node;
stack.count += 1;
}
fun pop(stack)
{
if stack.head is none:
return none; # return it; ?
value = stack.head.item;
stack.head = stack.head.prev;
stack.count -= 1;
return value;
}
push(stack, 1);
push(stack, 2);
push(stack, 3);
x = pop(stack);
y = pop(stack);
z = pop(stack);
w = pop(stack);
assert x == 3;
assert y == 2;
assert z == 1;
assert w == none;
+365
View File
@@ -0,0 +1,365 @@
#include <stdlib.h>
#include <string.h>
#include "../utils/defs.h"
#include "../utils/bucketlist.h"
#include "executable.h"
#define MAX_OPS 3
typedef struct {
Opcode opcode;
int offset;
int length;
union {
long long int as_int;
double as_float;
} operands[MAX_OPS];
} Instruction;
struct xExecutable {
int refs;
int headl, bodyl;
char *head;
Instruction *body;
Source *src;
};
struct xExeBuilder {
BucketList *data, *code;
int promc;
};
typedef struct {
const char *name;
int stacksz;
int opcount;
OperandType *optypes;
} InstrInfo;
static const InstrInfo instr_table[] = {
[OPCODE_NOPE] = {"NOPE", 0, 0, NULL},
[OPCODE_POS] = {"POS", 1, 0, NULL},
[OPCODE_NEG] = {"NEG", 1, 0, NULL},
[OPCODE_ADD] = {"ADD", 2, 0, NULL},
[OPCODE_SUB] = {"SUB", 2, 0, NULL},
[OPCODE_MUL] = {"MUL", 2, 0, NULL},
[OPCODE_DIV] = {"DIV", 2, 0, NULL},
[OPCODE_PUSHI] = {"PUSHI", 2, 1, (OperandType[]) {OPTP_INT}},
[OPCODE_PUSHF] = {"PUSHF", 2, 1, (OperandType[]) {OPTP_FLOAT}},
[OPCODE_PUSHS] = {"PUSHS", 2, 1, (OperandType[]) {OPTP_STRING}},
[OPCODE_PUSHV] = {"PUSHV", 2, 1, (OperandType[]) {OPTP_STRING}},
[OPCODE_RETURN] = {"RETURN", 0, 0, NULL},
};
static const char *operand_type_names[] = {
[OPTP_INT] = "int",
[OPTP_FLOAT] = "float",
[OPTP_STRING] = "string",
};
static const char *operand_type_arts[] = {
[OPTP_INT] = "an",
[OPTP_FLOAT] = "a",
[OPTP_STRING] = "a",
};
static unsigned int operand_type_sizes[] = {
[OPTP_INT] = membersizeof(Operand, as_int),
[OPTP_FLOAT] = membersizeof(Operand, as_float),
[OPTP_STRING] = membersizeof(Operand, as_string),
};
Executable *Executable_Copy(Executable *exe)
{
assert(exe != NULL);
exe->refs += 1;
return exe;
}
void Executable_Free(Executable *exe)
{
exe->refs -= 1;
assert(exe->refs >= 0);
if(exe->refs == 0)
{
if(exe->src)
Source_Free(exe->src);
free(exe);
}
}
_Bool Executable_SetSource(Executable *exe, Source *src)
{
src = Source_Copy(src);
if(src == NULL)
return 0;
exe->src = src;
return 1;
}
Source *Executable_GetSource(Executable *exe)
{
return exe->src;
}
_Bool Executable_Fetch(Executable *exe, int index, Opcode *opcode, Operand *ops, int *opc)
{
assert(index >= 0);
if(index >= exe->bodyl)
return 0;
const Instruction *instr = exe->body + index;
const InstrInfo *info = instr_table + instr->opcode;
if(opcode)
*opcode = instr->opcode;
int i;
if(ops && opc)
{
int k = MIN(*opc, info->opcount);
for(i = 0; i < k; i += 1)
{
OperandType type = info->optypes[i];
assert(type != OPTP_PROMISE);
switch(type) {
case OPTP_STRING:
int data_offset = instr->operands[i].as_int;
assert(data_offset < exe->headl);
ops[i].type = OPTP_STRING;
ops[i].as_string = exe->head + data_offset;
break;
case OPTP_INT:
ops[i].type = OPTP_INT;
ops[i].as_int = instr->operands[i].as_int;
break;
case OPTP_FLOAT:
ops[i].type = OPTP_FLOAT;
ops[i].as_int = instr->operands[i].as_int;
break;
case OPTP_PROMISE:
UNREACHABLE;
break;
}
}
*opc = MIN(*opc, info->opcount);
}
return 1;
}
ExeBuilder *ExeBuilder_New(BPAlloc *alloc)
{
assert(alloc != NULL);
ExeBuilder *exeb = BPAlloc_Malloc(alloc, sizeof(ExeBuilder));
if(exeb == NULL)
return NULL;
exeb->promc = 0;
exeb->data = BucketList_New(alloc);
exeb->code = BucketList_New(alloc);
if(exeb->data == NULL || exeb->code == NULL)
return NULL;
return exeb;
}
Executable *ExeBuilder_Finalize(ExeBuilder *exeb, Error *error)
{
assert(exeb != NULL);
if(exeb->promc > 0)
{
Error_Report(error, 0, "There are still %d unfulfilled promises", exeb->promc);
return 0;
}
Executable *exe;
{
int data_size = BucketList_Size(exeb->data);
int code_size = BucketList_Size(exeb->code);
assert(code_size % sizeof(Instruction) == 0);
void *temp = malloc(sizeof(Executable) + data_size + code_size);
if(temp == NULL)
{
Error_Report(error, 1, "No memory");
return NULL;
}
exe = temp;
exe->headl = data_size;
exe->bodyl = code_size / sizeof(Instruction);
exe->body = (Instruction*) (exe + 1);
exe->head = (char*) (exe->body + exe->bodyl);
exe->refs = 1;
exe->src = NULL;
}
BucketList_Copy(exeb->data, exe->head, -1);
BucketList_Copy(exeb->code, exe->body, -1);
return exe;
}
static void promise_callback(void *userp)
{
assert(userp != NULL);
ExeBuilder *exeb = userp;
exeb->promc -= 1;
assert(exeb->promc >= 0);
}
_Bool ExeBuilder_Append(ExeBuilder *exeb, Error *error, Opcode opcode, Operand *opv, int opc, int off, int len)
{
assert(exeb != NULL);
assert(opc >= 0);
const InstrInfo *info = instr_table + opcode;
if(opc != info->opcount)
{
// ERROR: Too many operands were provided.
Error_Report(error, 0,
"Instruction %s expects %d operands, but %d were provided",
info->name, opc, info->opcount);
return 0;
}
assert(opc <= MAX_OPS);
{
Instruction *instr = BucketList_Append2(exeb->code, NULL, sizeof(Instruction));
if(instr == NULL)
{
Error_Report(error, 1, "No memory");
return 0;
}
instr->opcode = opcode;
instr->offset = off;
instr->length = len;
for(int i = 0; i < opc; i += 1)
{
// Check that the expected type and the provided one
// match, or that the provided type is a promise with
// the same size of the expected type.
{
OperandType expected_type = info->optypes[i];
OperandType provided_type = opv[i].type;
assert(expected_type != OPTP_PROMISE);
if(provided_type == OPTP_PROMISE)
{
assert(opv[i].as_promise != NULL);
if(expected_type == OPTP_STRING)
{
Error_Report(error, 0, "Promise values can't be provided as string operands");
return 0;
}
if(Promise_Size(opv[i].as_promise) != operand_type_sizes[expected_type])
{
Error_Report(error, 0,
"Provided promise has a value size of %d, "
"but since %s %s was expected, the promise "
"size must be %d",
Promise_Size(opv[i].as_promise),
operand_type_arts[expected_type],
operand_type_names[expected_type],
operand_type_sizes[expected_type]);
return 0;
}
}
else if(expected_type != provided_type)
{
// ERROR: Wrong operand type provided.
Error_Report(error, 0,
"Instruction %s expects %s %s as operand %d, but %s %s was provided instead",
info->name,
operand_type_arts[expected_type],
operand_type_names[expected_type],
i,
operand_type_arts[provided_type],
operand_type_names[provided_type]
);
return 0;
}
}
// Do the copying of the operands.
switch(opv[i].type)
{
case OPTP_STRING:
instr->operands[i].as_int = BucketList_Size(exeb->data);
if(!BucketList_Append(exeb->data, opv[i].as_string, strlen(opv[i].as_string)+1))
{
Error_Report(error, 1, "No memory");
return 0;
}
break;
case OPTP_PROMISE:
assert(info->optypes[i] != OPTP_STRING);
// This must be incremented before subscribing
// since the counter-decrementing callback may
// be called immediately if the promise was
// already fulfilled.
exeb->promc += 1;
if(!Promise_Subscribe2(opv[i].as_promise, &instr->operands[i], exeb, promise_callback))
{
Error_Report(error, 1, "No memory");
return 0;
}
break;
case OPTP_INT:
instr->operands[i].as_int = opv[i].as_int;
break;
case OPTP_FLOAT:
instr->operands[i].as_float = opv[i].as_float;
break;
}
}
}
return 1;
}
+50
View File
@@ -0,0 +1,50 @@
#ifndef EXECUTABLE_H
#define EXECUTABLE_H
#include "../utils/source.h"
#include "../utils/promise.h"
typedef enum {
OPTP_INT,
OPTP_FLOAT,
OPTP_STRING,
OPTP_PROMISE,
} OperandType;
typedef struct {
OperandType type;
union {
long long int as_int;
double as_float;
const char *as_string;
Promise *as_promise;
};
} Operand;
typedef enum {
OPCODE_NOPE,
OPCODE_POS,
OPCODE_NEG,
OPCODE_ADD,
OPCODE_SUB,
OPCODE_MUL,
OPCODE_DIV,
OPCODE_PUSHI,
OPCODE_PUSHF,
OPCODE_PUSHS,
OPCODE_PUSHV,
OPCODE_RETURN,
} Opcode;
typedef struct xExecutable Executable;
typedef struct xExeBuilder ExeBuilder;
Executable *Executable_Copy(Executable *exe);
void Executable_Free(Executable *exe);
_Bool Executable_Fetch(Executable *exe, int index, Opcode *opcode, Operand *ops, int *opc);
_Bool Executable_SetSource(Executable *exe, Source *src);
Source *Executable_GetSource(Executable *exe);
ExeBuilder *ExeBuilder_New(BPAlloc *alloc);
_Bool ExeBuilder_Append(ExeBuilder *exeb, Error *error, Opcode opcode, Operand *opv, int opc, int off, int len);
Executable *ExeBuilder_Finalize(ExeBuilder *exeb, Error *error);
#endif
+4
View File
@@ -0,0 +1,4 @@
#ifndef AST_H
#define AST_H
typedef struct xAST AST;
#endif
+73
View File
@@ -0,0 +1,73 @@
#ifndef ASTi_H
#define ASTi_H
#include "../utils/source.h"
#include "AST.h"
typedef enum {
NODE_EXPR,
} NodeKind;
typedef enum {
EXPR_POS,
EXPR_NEG,
EXPR_ADD,
EXPR_SUB,
EXPR_MUL,
EXPR_DIV,
EXPR_INT,
EXPR_FLOAT,
EXPR_STRING,
EXPR_IDENT,
} ExprKind;
typedef struct Node Node;
struct xAST {
Source *src;
Node *root;
};
struct Node {
NodeKind kind;
Node *next;
int offset,
length;
char body[];
};
typedef struct {
Node base;
ExprKind kind;
char body[];
} ExprNode;
typedef struct {
ExprNode base;
long long int val;
} IntExprNode;
typedef struct {
ExprNode base;
double val;
} FloatExprNode;
typedef struct {
ExprNode base;
char *val;
int len;
} StringExprNode;
typedef struct {
ExprNode base;
char *val;
int len;
} IdentExprNode;
typedef struct {
ExprNode base;
Node *head;
int count;
} OperExprNode;
#endif
+123
View File
@@ -0,0 +1,123 @@
#include <assert.h>
#include <setjmp.h>
#include <stdlib.h>
#include "../utils/defs.h"
#include "compile.h"
#include "ASTi.h"
static Opcode exprkind_to_opcode(ExprKind kind)
{
switch(kind)
{
case EXPR_POS: return OPCODE_POS;
case EXPR_NEG: return OPCODE_NEG;
case EXPR_ADD: return OPCODE_ADD;
case EXPR_SUB: return OPCODE_SUB;
case EXPR_MUL: return OPCODE_MUL;
case EXPR_DIV: return OPCODE_DIV;
default:
UNREACHABLE;
break;
}
}
static _Bool emit_instr_for_node(ExeBuilder *exeb, Node *node, Error *error)
{
assert(node != NULL);
switch(node->kind)
{
case NODE_EXPR:
{
ExprNode *expr = (ExprNode*) node;
switch(expr->kind)
{
case EXPR_POS:
case EXPR_NEG:
case EXPR_ADD:
case EXPR_SUB:
case EXPR_MUL:
case EXPR_DIV:
{
OperExprNode *oper = (OperExprNode*) expr;
for(Node *operand = oper->head; operand; operand = operand->next)
emit_instr_for_node(exeb, operand, error);
return ExeBuilder_Append(exeb, error,
exprkind_to_opcode(expr->kind),
NULL, 0, node->offset, node->length);
}
case EXPR_INT:
{
IntExprNode *p = (IntExprNode*) expr;
Operand op = { .type = OPTP_INT, .as_int = p->val };
return ExeBuilder_Append(exeb, error, OPCODE_PUSHI, &op, 1, node->offset, node->length);
}
case EXPR_FLOAT:
{
FloatExprNode *p = (FloatExprNode*) expr;
Operand op = { .type = OPTP_FLOAT, .as_float = p->val };
return ExeBuilder_Append(exeb, error, OPCODE_PUSHF, &op, 1, node->offset, node->length);
}
case EXPR_STRING:
{
StringExprNode *p = (StringExprNode*) expr;
Operand op = { .type = OPTP_STRING, .as_string = p->val };
return ExeBuilder_Append(exeb, error, OPCODE_PUSHS, &op, 1, node->offset, node->length);
}
case EXPR_IDENT:
{
IdentExprNode *p = (IdentExprNode*) expr;
Operand op = { .type = OPTP_STRING, .as_string = p->val };
return ExeBuilder_Append(exeb, error, OPCODE_PUSHV, &op, 1, node->offset, node->length);
}
default:
UNREACHABLE;
break;
}
break;
}
default:
UNREACHABLE;
break;
}
}
Executable *compile(AST *ast, BPAlloc *alloc, Error *error)
{
assert(ast != NULL);
assert(error != NULL);
BPAlloc *alloc2 = alloc;
if(alloc2 == NULL)
{
alloc2 = BPAlloc_Init(-1);
if(alloc2 == NULL)
return NULL;
}
Executable *exe = NULL;
ExeBuilder *exeb = ExeBuilder_New(alloc2);
if(exeb)
{
if(!emit_instr_for_node(exeb, ast->root, error))
return 0;
if(ExeBuilder_Append(exeb, error, OPCODE_RETURN, NULL, 0, Source_GetSize(ast->src), 0))
exe = ExeBuilder_Finalize(exeb, error);
}
if(alloc == NULL)
BPAlloc_Free(alloc2);
return exe;
}
+8
View File
@@ -0,0 +1,8 @@
#ifndef COMPILE_H
#define COMPILE_H
#include "../utils/error.h"
#include "../utils/bpalloc.h"
#include "../common/executable.h"
#include "AST.h"
Executable *compile(AST *ast, BPAlloc *alloc, Error *error);
#endif
+762
View File
@@ -0,0 +1,762 @@
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#include <ctype.h>
#include "parse.h"
#include "ASTi.h"
#define UNREACHABLE assert(0)
#ifndef NULL
#define NULL ((void*) 0)
#endif
typedef enum {
TPOS = '+',
TNEG = '-',
TADD = '+',
TSUB = '-',
TMUL = '*',
TDIV = '/',
TASS = '=',
TDONE = 256,
TINT,
TFLOAT,
TSTRING,
TIDENT,
} TokenKind;
typedef struct Token Token;
struct Token {
TokenKind kind;
Token *prev, *next;
int offset, length;
};
typedef struct {
const char *src;
Token *token;
BPAlloc *alloc;
Error *error;
} Context;
static Node *parse_statement(Context *ctx);
static Node *parse_expression(Context *ctx);
static Node *parse_expression_statement(Context *ctx);
static inline _Bool isoper(char c)
{
return c == '+' ||
c == '-' ||
c == '*' ||
c == '/' ||
c == '=';
}
AST *parse(Source *src, BPAlloc *alloc, Error *error)
{
assert(src != NULL);
assert(alloc != NULL);
const char *str = Source_GetBody(src);
int len = Source_GetSize(src);
assert(str != NULL);
assert(len >= 0);
AST *ast = BPAlloc_Malloc(alloc, sizeof(AST));
if(ast == NULL)
return NULL;
Token *head = NULL,
*tail = NULL;
int i = 0;
while(1)
{
// Skip whitespace and comments.
while(i < len && (isspace(str[i]) || str[i] == '#'))
{
while(i < len && isspace(str[i]))
i += 1;
if(str[i] == '#')
{
i += 1;
while(i < len && str[i] != '\n')
i += 1;
}
}
if(i == len)
break; // No more tokens left.
Token *tok = BPAlloc_Malloc(alloc, sizeof(Token)); // Allocate a token.
if(tok == NULL)
{
// Error: No memory.
Error_Report(error, 1, "No memory");
return NULL;
}
if(isalpha(str[i]) || str[i] == '_')
{
tok->kind = TIDENT;
tok->offset = i;
while(i < len && (isalpha(str[i]) || str[i] == '_'))
i += 1;
tok->length = i - tok->offset;
}
else if(isdigit(str[i]))
{
tok->kind = TINT;
tok->offset = i;
while(i < len && isdigit(str[i]))
i += 1;
if(i+1 < len && str[i] == '.' && isdigit(str[i+1]))
{
i += 1; // Consume the dot.
tok->kind = TFLOAT;
while(i < len && isdigit(str[i]))
i += 1;
}
tok->length = i - tok->offset;
}
else if(str[i] == '\'' || str[i] == '"')
{
tok->kind = TSTRING;
tok->offset = i;
char f = str[i];
i += 1; // Skip the starting quote.
while(i < len && str[i] != f)
i += 1;
if(i == len)
{
Error_Report(error, 0, "Source ended inside string literal");
return NULL;
}
i += 1; // Consume the ' or ".
tok->length = i - tok->offset;
}
else if(isoper(str[i]))
{
tok->offset = i;
while(i < len && isoper(str[i]))
i += 1;
tok->length = i - tok->offset;
// Determine the token
#define matchop(str, len, const_str) \
(len == sizeof(const_str)-1 && !strncmp(str, const_str, sizeof(const_str)-1))
if(matchop(str + tok->offset, tok->length, "+"))
{
tok->kind = TADD;
}
else if(matchop(str + tok->offset, tok->length, "-"))
{
tok->kind = TSUB;
}
else if(matchop(str + tok->offset, tok->length, "*"))
{
tok->kind = TMUL;
}
else if(matchop(str + tok->offset, tok->length, "/"))
{
tok->kind = TDIV;
}
else if(matchop(str + tok->offset, tok->length, "="))
{
tok->kind = TASS;
}
else
{
// Not a known operator.
tok->kind = str[tok->offset];
tok->length = 1;
i = tok->offset + 1;
}
#undef matchop
}
else
{
tok->kind = str[i];
tok->offset = i;
tok->length = 1;
i += 1;
}
// Append to the token list.
if(head)
tail->next = tok;
else
head = tok;
tok->prev = tail;
tok->next = NULL;
tail = tok;
}
{
Token *tok = BPAlloc_Malloc(alloc, sizeof(Token)); // Allocate a token.
if(tok == NULL)
{
// Error: No memory.
Error_Report(error, 1, "No memory");
return NULL;
}
tok->kind = TDONE;
tok->offset = i;
tok->length = 0;
if(head)
tail->next = tok;
else
head = tok;
tok->prev = tail;
tok->next = NULL;
tail = tok;
}
Context ctx;
ctx.src = str;
ctx.token = head;
ctx.alloc = alloc;
ctx.error = error;
Node *root = parse_statement(&ctx);
if(root == NULL)
return NULL;
ast->src = src; // Not copying! Be sure to not free the source before the AST!
ast->root = root;
if(ast->src == NULL)
return NULL;
return ast;
}
static Node *parse_statement(Context *ctx)
{
assert(ctx != NULL);
switch(ctx->token->kind)
{
case '*':
case '/':
case TASS:
case TDONE:
UNREACHABLE;
break;
case '(':
case '+':
case '-':
case TINT:
case TFLOAT:
case TSTRING:
case TIDENT:
return parse_expression_statement(ctx);
}
Error_Report(ctx->error, 0, "Got token \"%.*s\" where the start of a statement was expected",
ctx->token->length, ctx->src + ctx->token->offset);
return NULL;
}
static Node *parse_expression_statement(Context *ctx)
{
assert(ctx != NULL);
Node *expr = parse_expression(ctx);
if(expr == NULL)
return NULL;
assert(ctx->token != NULL);
if(ctx->token->kind == TDONE)
// If the source ended before
// the final ";", it's ok.
return expr;
if(ctx->token->kind != ';')
{
// ERROR: Got something other than
// a semicolon at the end of statement.
Error_Report(ctx->error, 0, "Got token \"%.*s\" where \";\" was expected", ctx->token->length, ctx->src + ctx->token->offset);
return NULL;
}
ctx->token = ctx->token->next;
assert(ctx->token != NULL);
return expr;
}
static Node *parse_string_primary_expression(Context *ctx)
{
assert(ctx != NULL);
assert(ctx->token->kind == TSTRING);
const char *src = ctx->src;
int len = ctx->token->offset + ctx->token->length - 1;
int i = ctx->token->offset + 1;
assert(src[i-1] == '"' || src[i-1] == '\'');
assert(src[len] == '"' || src[len] == '\'');
char temp[4096];
int temp_used = 0;
do
{
int segm_off, segm_len;
{
segm_off = i;
while(i < len && src[i] != '\\')
i += 1;
segm_len = i - segm_off;
}
if(temp_used + segm_len >= (int) sizeof(temp))
{
Error_Report(ctx->error, 1, "String is too big to be rendered inside the fixed size buffer");
return NULL;
}
memcpy(temp + temp_used, src + segm_off, segm_len);
temp_used += segm_len;
if(src[i] == '\\')
{
i += 1; // Consume the \.
if(temp_used + 1 >= (int) sizeof(temp))
{
Error_Report(ctx->error, 1, "String is too big to be rendered inside the fixed size buffer");
return NULL;
}
if(i == len)
{
// Append the \ as a normal char.
temp[temp_used++] = '\\';
}
else
{
switch(src[i])
{
case 'n': temp[temp_used++] = '\n'; break;
case 't': temp[temp_used++] = '\t'; break;
case 'r': temp[temp_used++] = '\r'; break;
default:
Error_Report(ctx->error, 0, "Invalid escape sequence \\%c", src[i]);
return NULL;
}
i += 1; // Consume the char after the \.
}
}
}
while(i < len);
assert(temp_used < (int) sizeof(temp));
temp[temp_used] = '\0';
char *copy;
int copyl;
{
copy = BPAlloc_Malloc(ctx->alloc, temp_used + 1);
if(copy == NULL)
{
Error_Report(ctx->error, 1, "No memory");
return NULL;
}
strcpy(copy, temp);
copyl = temp_used;
}
StringExprNode *node;
{
node = BPAlloc_Malloc(ctx->alloc, sizeof(StringExprNode));
if(node == NULL)
{
Error_Report(ctx->error, 1, "No memory");
return NULL;
}
node->base.base.kind = NODE_EXPR;
node->base.base.next = NULL;
node->base.base.offset = ctx->token->offset;
node->base.base.length = ctx->token->length;
node->base.kind = EXPR_STRING;
node->val = copy;
node->len = copyl;
}
ctx->token = ctx->token->next;
assert(ctx->token != NULL);
return (Node*) node;
}
static Node *parse_primary_expresion(Context *ctx)
{
assert(ctx != NULL);
switch(ctx->token->kind)
{
case '+':
case '-':
{
Token *operator = ctx->token;
ctx->token = ctx->token->next;
assert(ctx->token != NULL);
Node *operand = parse_primary_expresion(ctx);
if(operand == NULL)
return NULL;
OperExprNode *temp = BPAlloc_Malloc(ctx->alloc, sizeof(OperExprNode));
{
if(temp == NULL)
return NULL;
temp->base.base.kind = NODE_EXPR;
temp->base.base.next = NULL;
temp->base.base.offset = operator->offset;
temp->base.base.length = operand->offset + operand->length - operator->offset;
temp->base.kind = operator->kind == '+' ? EXPR_POS : EXPR_NEG;
temp->head = operand;
temp->count = 1;
}
return (Node*) temp;
}
case '(':
{
ctx->token = ctx->token->next;
Node *node = parse_expression(ctx);
if(node == NULL)
return NULL;
if(ctx->token->kind != ')')
{
Error_Report(ctx->error, 0, "Missing \")\", after sub-expression");
return NULL;
}
ctx->token = ctx->token->next;
assert(ctx->token != NULL);
return node;
}
case TINT:
{
char buffer[64];
if(ctx->token->length >= (int) sizeof(buffer))
{
Error_Report(ctx->error, 1, "Integer is too big");
return NULL;
}
memcpy(buffer, ctx->src + ctx->token->offset, ctx->token->length);
buffer[ctx->token->length] = '\0';
errno = 0;
long long int val = strtoll(buffer, NULL, 10);
if(errno == ERANGE)
{
Error_Report(ctx->error, 1, "Integer is too big");
return NULL;
}
else assert(errno == 0);
IntExprNode *node;
{
node = BPAlloc_Malloc(ctx->alloc, sizeof(IntExprNode));
if(node == NULL)
{
Error_Report(ctx->error, 1, "No memory");
return NULL;
}
node->base.base.kind = NODE_EXPR;
node->base.base.next = NULL;
node->base.base.offset = ctx->token->offset;
node->base.base.length = ctx->token->length;
node->base.kind = EXPR_INT;
node->val = val;
}
ctx->token = ctx->token->next;
assert(ctx->token != NULL);
return (Node*) node;
}
case TFLOAT:
{
char buffer[64];
if(ctx->token->length >= (int) sizeof(buffer))
{
Error_Report(ctx->error, 1, "Floating is too big");
return NULL;
}
memcpy(buffer, ctx->src + ctx->token->offset, ctx->token->length);
buffer[ctx->token->length] = '\0';
errno = 0;
double val = strtod(buffer, NULL);
if(errno == ERANGE)
{
Error_Report(ctx->error, 1, "Floating is too big");
return NULL;
}
else assert(errno == 0);
FloatExprNode *node;
{
node = BPAlloc_Malloc(ctx->alloc, sizeof(FloatExprNode));
if(node == NULL)
{
Error_Report(ctx->error, 1, "No memory");
return NULL;
}
node->base.base.kind = NODE_EXPR;
node->base.base.next = NULL;
node->base.base.offset = ctx->token->offset;
node->base.base.length = ctx->token->length;
node->base.kind = EXPR_FLOAT;
node->val = val;
}
ctx->token = ctx->token->next;
assert(ctx->token != NULL);
return (Node*) node;
}
case TSTRING:
return parse_string_primary_expression(ctx);
case TIDENT:
{
char *copy;
int copyl;
{
copy = BPAlloc_Malloc(ctx->alloc, ctx->token->length + 1);
if(copy == NULL)
{
Error_Report(ctx->error, 1, "No memory");
return NULL;
}
copyl = ctx->token->length;
memcpy(copy, ctx->src + ctx->token->offset, copyl);
copy[copyl] = '\0';
}
IdentExprNode *node;
{
node = BPAlloc_Malloc(ctx->alloc, sizeof(IdentExprNode));
if(node == NULL)
{
Error_Report(ctx->error, 1, "No memory");
return NULL;
}
node->base.base.kind = NODE_EXPR;
node->base.base.next = NULL;
node->base.base.offset = ctx->token->offset;
node->base.base.length = ctx->token->length;
node->base.kind = EXPR_IDENT;
node->val = copy;
node->len = copyl;
}
ctx->token = ctx->token->next;
assert(ctx->token != NULL);
return (Node*) node;
}
case TDONE:
Error_Report(ctx->error, 1, "Unexpected end of source where a primary expression was expected");
return NULL;
default:
Error_Report(ctx->error, 1, "Unexpected token \"%.*s\" where a primary expression was expected", ctx->token->length, ctx->src + ctx->token->offset);
return NULL;
}
UNREACHABLE;
return NULL;
}
static inline _Bool isbinop(Token *tok)
{
assert(tok != NULL);
return tok->kind == '+' ||
tok->kind == '-' ||
tok->kind == '*' ||
tok->kind == '/';
}
static inline _Bool isrightassoc(Token *tok)
{
assert(tok != NULL);
(void) tok;
return 0;
}
static inline int precedenceof(Token *tok)
{
assert(tok != NULL);
switch(tok->kind)
{
case '+':
case '-':
return 1;
case '*':
case '/':
return 2;
default:
return -10000000;
}
UNREACHABLE;
return -100000000;
}
static Node *parse_expression_2(Context *ctx, Node *left_expr, int min_prec)
{
while(isbinop(ctx->token) && precedenceof(ctx->token) >= min_prec)
{
Token *op = ctx->token;
ctx->token = ctx->token->next;
assert(ctx->token != NULL);
Node *right_expr = parse_primary_expresion(ctx);
if(right_expr == NULL)
return NULL;
while(isbinop(ctx->token) && (precedenceof(ctx->token) > precedenceof(op) || (precedenceof(ctx->token) == precedenceof(op) && isrightassoc(ctx->token))))
{
right_expr = parse_expression_2(ctx, right_expr, precedenceof(op) + 1);
if(right_expr == NULL)
return NULL;
}
OperExprNode *temp;
{
temp = BPAlloc_Malloc(ctx->alloc, sizeof(OperExprNode));
if(temp == NULL)
{
Error_Report(ctx->error, 1, "No memory");
return NULL;
}
temp->base.base.kind = NODE_EXPR;
temp->base.base.next = NULL;
temp->base.base.offset = left_expr->offset;
temp->base.base.length = right_expr->offset + right_expr->length - left_expr->offset;
switch(op->kind)
{
case '+': temp->base.kind = EXPR_ADD; break;
case '-': temp->base.kind = EXPR_SUB; break;
case '*': temp->base.kind = EXPR_MUL; break;
case '/': temp->base.kind = EXPR_DIV; break;
default:
UNREACHABLE;
break;
}
temp->head = left_expr;
temp->head->next = right_expr;
temp->count = 2;
assert(right_expr->next == NULL);
}
left_expr = (Node*) temp;
}
return left_expr;
}
static Node *parse_expression(Context *ctx)
{
Node *left_expr = parse_primary_expresion(ctx);
if(left_expr == NULL)
return NULL;
if(ctx->token->kind == TDONE)
return left_expr;
return parse_expression_2(ctx, left_expr, -1000000000);
}
+8
View File
@@ -0,0 +1,8 @@
#ifndef PARSE_H
#define PARSE_H
#include "../utils/bpalloc.h"
#include "../utils/source.h"
#include "../utils/error.h"
#include "AST.h"
AST *parse(Source *src, BPAlloc *alloc, Error *error);
#endif
+161
View File
@@ -0,0 +1,161 @@
#include <assert.h>
#include <xjson.h>
#include "serialize.h"
#include "ASTi.h"
#define UNREACHABLE assert(0)
static xj_value *convert_node(Node *node, xj_error *error, xj_alloc **alloc);
char *serialize(AST *ast, int *len)
{
xj_alloc *alloc = NULL;
xj_error error;
xj_value *value = convert_node(ast->root, &error, &alloc);
if(value == NULL)
{
xj_free(alloc);
return NULL;
}
else
{
char *serialized = xj_encode(value, len);
xj_free(alloc);
return serialized;
}
}
static const char *expr_kind_name(ExprNode *expr)
{
switch(expr->kind)
{
case EXPR_POS: return "pos";
case EXPR_NEG: return "neg";
case EXPR_ADD: return "add";
case EXPR_SUB: return "sub";
case EXPR_MUL: return "mul";
case EXPR_DIV: return "div";
case EXPR_INT: return "int";
case EXPR_FLOAT: return "float";
case EXPR_STRING: return "string";
case EXPR_IDENT: return "ident";
default:
UNREACHABLE;
return NULL;
}
UNREACHABLE;
return NULL;
}
static xj_value *convert_node_list(Node *head, xj_error *error, xj_alloc **alloc);
static xj_value *convert_node(Node *node, xj_error *error, xj_alloc **alloc)
{
switch(node->kind)
{
case NODE_EXPR:
ExprNode *expr = (ExprNode*) node;
switch(expr->kind)
{
case EXPR_POS:
case EXPR_NEG:
case EXPR_ADD:
case EXPR_SUB:
case EXPR_MUL:
case EXPR_DIV:
{
OperExprNode *oper = (OperExprNode*) expr;
xj_value *operands = NULL;
if(oper->count > 0)
{
operands = convert_node_list((Node*) oper->head, error, alloc);
if(operands == NULL)
return NULL;
}
return xj_decodef(error, alloc,
"{"
"\t\"node-kind\": \"expr\", \n"
"\t\"expr-kind\": %Q, \n"
"\t\"operands\": %L\n"
"}", expr_kind_name(expr), operands);
}
case EXPR_INT:
return xj_decodef(error, alloc,
"{"
"\t\"node-kind\": \"expr\", \n"
"\t\"expr-kind\": %Q, \n"
"\t\"value\": %d\n"
"}", expr_kind_name(expr), ((IntExprNode*) expr)->val);
case EXPR_FLOAT:
return xj_decodef(error, alloc,
"{"
"\t\"node-kind\": \"expr\", \n"
"\t\"expr-kind\": %Q, \n"
"\t\"value\": %f\n"
"}", expr_kind_name(expr), ((FloatExprNode*) expr)->val);
case EXPR_STRING:
return xj_decodef(error, alloc,
"{"
"\t\"node-kind\": \"expr\", \n"
"\t\"expr-kind\": %Q, \n"
"\t\"value\": %Q\n"
"}", expr_kind_name(expr), ((StringExprNode*) expr)->val);
case EXPR_IDENT:
return xj_decodef(error, alloc,
"{"
"\t\"node-kind\": \"expr\", \n"
"\t\"expr-kind\": %Q, \n"
"\t\"value\": %Q\n"
"}", expr_kind_name(expr), ((IdentExprNode*) expr)->val);
default:
UNREACHABLE;
break;
}
break;
default:
UNREACHABLE;
break;
}
UNREACHABLE;
return NULL;
}
static xj_value *convert_node_list(Node *head, xj_error *error, xj_alloc **alloc)
{
xj_value *json_head = NULL;
xj_value **json_tail = &json_head;
Node *curs = head;
while(curs)
{
xj_value *temp = convert_node(curs, error, alloc);
if(temp == NULL)
return NULL;
temp->next = NULL;
*json_tail = temp;
json_tail = &temp->next;
curs = curs->next;
}
return json_head;
}
+5
View File
@@ -0,0 +1,5 @@
#ifndef SERIALIZE_H
#define SERIALIZE_H
#include "AST.h"
char *serialize(AST *ast, int *len);
#endif
+335
View File
@@ -0,0 +1,335 @@
#include <assert.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include "compiler/parse.h"
#include "compiler/serialize.h"
#include "compiler/compile.h"
#include "runtime/runtime.h"
#include "runtime/runtime_error.h"
/* $ noja -f <file>
* $ noja -c <text>
* $ noja -f <file> -o <file>
*/
typedef enum { RUN, HELP, PARSE, VERSION, DISASSEMBLY } Action;
typedef struct {
Action action;
_Bool input_is_file, debug, verbose;
const char *input, *output;
} Options;
static int parse_args(Options *opts, int argc, char **argv, Error *error)
{
assert(argc >= 0);
assert(opts != NULL);
opts->action = RUN;
opts->input_is_file = 1;
opts->input = NULL;
opts->output = NULL;
int i;
for(i = 1; i < argc; i += 1)
{
if(!strcmp("-r", argv[i]) || !strcmp("--run", argv[i]))
{
opts->action = RUN;
}
else if(!strcmp("-p", argv[i]) || !strcmp("--parse", argv[i]))
{
opts->action = PARSE;
}
else if(!strcmp("-d", argv[i]) || !strcmp("--disassembly", argv[i]))
{
opts->action = DISASSEMBLY;
}
else if(!strcmp("-h", argv[i]) || !strcmp("--help", argv[i]))
{
opts->action = HELP;
}
else if(!strcmp("-v", argv[i]) || !strcmp("--version", argv[i]))
{
opts->action = VERSION;
}
else if(!strcmp("--debug", argv[i]))
{
opts->debug = 1;
}
else if(!strcmp("--verbose", argv[i]))
{
opts->verbose = 1;
}
else if(!strcmp("-f", argv[i]))
{
opts->input_is_file = 1;
}
else if(!strcmp("-c", argv[i]))
{
opts->input_is_file = 0;
}
else if(!strcmp("-o", argv[i]))
{
i += 1;
if(i == argc)
{
Error_Report(error, 0, "Option -o requires a file name after it");
return -1;
}
opts->output = argv[i];
}
else break;
}
if(i == argc)
{
Error_Report(error, 0, "Input file is missing");
return -1;
}
opts->input = argv[i];
i += 1;
return i;
}
int main(int argc, char **argv)
{
Error error;
Error_Init(&error);
// Parse command line options.
Options opts;
{
int i = parse_args(&opts, argc, argv, &error);
if(i < 0)
{
fprintf(stderr, "FATAL: %s.\n", error.message);
Error_Free(&error);
return 1;
}
}
switch(opts.action)
{
case HELP:
fprintf(stderr, "Not implemented yet.\n");
return 1;
case VERSION:
fprintf(stderr, "Not implemented yet.\n");
return 1;
case RUN:
{
// Load
Source *src;
{
if(opts.input_is_file)
src = Source_FromFile(opts.input, &error);
else
src = Source_FromString(NULL, opts.input, -1, &error);
if(src == NULL)
{
fprintf(stderr, "FATAL: %s.\n", error.message);
return 1;
}
}
// Compile
Executable *exe;
{
BPAlloc *alloc = BPAlloc_Init(-1);
if(alloc == NULL)
return 1;
AST *ast = parse(src, alloc, &error);
if(ast == NULL)
{
fprintf(stderr, "PARSING ERROR: %s.\n", error.message);
Error_Free(&error);
BPAlloc_Free(alloc);
Source_Free(src);
return 1;
}
exe = compile(ast, alloc, &error);
if(exe == NULL)
{
fprintf(stderr, "COMPILATION ERROR: %s.\n", error.message);
Error_Free(&error);
BPAlloc_Free(alloc);
Source_Free(src);
return 1;
}
BPAlloc_Free(alloc);
}
// Execute
{
Runtime *runtime = Runtime_New(-1, -1);
if(runtime == NULL)
{
fprintf(stderr, "Couldn't initialize runtime.\n");
Executable_Free(exe);
Source_Free(src);
return 1;
}
RuntimeError error;
RuntimeError_Init(&error, runtime);
Object *result = run(runtime, (Error*) &error, exe, 0, NULL, 0);
if(result == NULL)
{
fprintf(stderr, "RUNTIME ERROR: %s.\n", error.base.message);
if(error.snapshot)
Snapshot_Print(error.snapshot, stderr);
else
fprintf(stderr, "No snapshot available.\n");
Source_Free(src);
Executable_Free(exe);
RuntimeError_Free(&error);
Runtime_Free(runtime);
return 1;
}
else
{
if(Object_IsInt(result))
{
long long int val = Object_ToInt(result, (Error*) &error);
assert(error.base.occurred == 0);
fprintf(stderr, "%lld\n", val);
}
else if(Object_IsFloat(result))
{
double val = Object_ToFloat(result, (Error*) &error);
assert(error.base.occurred == 0);
fprintf(stderr, "%f\n", val);
}
else
{
fprintf(stderr, "Not printing returned value since it's not an int or a float.\n");
}
}
Runtime_Free(runtime);
}
Executable_Free(exe);
Source_Free(src);
break;
}
case PARSE:
{
// Load
Source *src;
{
if(opts.input_is_file)
src = Source_FromFile(opts.input, &error);
else
src = Source_FromString(NULL, opts.input, -1, &error);
if(src == NULL)
{
fprintf(stderr, "FATAL: %s.\n", error.message);
return 1;
}
}
BPAlloc *alloc = BPAlloc_Init(-1);
if(alloc == NULL)
return 1;
Error error;
Error_Init(&error);
AST *ast = parse(src, alloc, &error);
if(ast == NULL)
{
fprintf(stderr, "PARSING ERROR: %s.\n", error.message);
Error_Free(&error);
BPAlloc_Free(alloc);
Source_Free(src);
return 1;
}
int len;
char *str = serialize(ast, &len);
_Bool failed = 0;
if(str == NULL)
{
fprintf(stderr, "Failed to serialize\n");
failed = 1;
}
else
{
if(opts.output == NULL)
{
fprintf(stdout, "%s\n", str);
}
else
{
FILE *fp = fopen(opts.output, "wb");
if(fp == NULL)
{
fprintf(stderr, "Couldn't write to \"%s\".\n", opts.output);
failed = 1;
}
else
{
int k = fwrite(str, 1, len, fp);
if(k != len)
{
fprintf(stderr, "Only %d bytes out of %d could be written to \"%s\".\n", k, len, opts.output);
failed = 1;
}
}
}
free(str);
}
Source_Free(src);
BPAlloc_Free(alloc);
return failed;
}
case DISASSEMBLY:
fprintf(stderr, "Not implemented yet.\n");
return 1;
}
return 0;
}
+110
View File
@@ -0,0 +1,110 @@
#include <stdint.h>
#include <assert.h>
#include <string.h>
#include <stdlib.h>
#include "objects.h"
typedef struct OflowAlloc OflowAlloc;
struct OflowAlloc {
OflowAlloc *prev;
char body[];
};
struct xHeap {
int size;
int used;
void *body;
OflowAlloc *oflow;
};
Heap *Heap_New(int size)
{
if(size < 0)
size = 65536;
Heap *heap = malloc(sizeof(Heap));
if(heap == NULL)
return NULL;
heap->size = size;
heap->used = 0;
heap->body = malloc(size);
heap->oflow = 0;
if(heap->body == NULL)
{
free(heap);
return NULL;
}
return heap;
}
void Heap_Free(Heap *heap)
{
while(heap->oflow)
{
OflowAlloc *prev = heap->oflow->prev;
free(heap->oflow);
heap->oflow = prev;
}
free(heap->body);
free(heap);
}
void *Heap_Malloc(Heap *heap, const Type *type, Error *err)
{
void *addr = Heap_RawMalloc(heap, type->size, err);
if(addr == NULL)
return NULL;
Object *obj = addr;
obj->type = type;
obj->flags = 0;
if(type->init && !type->init(obj, err))
return NULL;
obj->type = type;
obj->flags = 0;
return (Object*) addr;
}
void *Heap_RawMalloc(Heap *heap, int size, Error *err)
{
assert(err);
assert(heap);
assert(size > -1);
void *addr;
if(heap->used + size >= heap->size)
{
OflowAlloc *oflow = malloc(sizeof(OflowAlloc) + size);
if(oflow == 0)
return 0;
oflow->prev = heap->oflow;
heap->oflow = oflow;
addr = oflow->body;
}
else
{
if(heap->used & 7)
heap->used = (heap->used & ~7) + 8;
addr = heap->body + heap->used;
heap->used += size;
}
assert(((intptr_t) addr) % 8 == 0);
return addr;
}
+40
View File
@@ -0,0 +1,40 @@
#include <assert.h>
#include "objects.h"
static double to_float(Object *obj, Error *err);
typedef struct {
Object base;
double val;
} FloatObject;
static const Type t_float = {
.base = (Object) { .type = &t_type, .flags = Object_STATIC },
.name = "float",
.size = sizeof (FloatObject),
.atomic = ATMTP_FLOAT,
.to_float = to_float,
};
static double to_float(Object *obj, Error *err)
{
assert(obj);
assert(err);
assert(Object_GetType(obj) == &t_float);
(void) err;
return ((FloatObject*) obj)->val;
}
Object *Object_FromFloat(double val, Heap *heap, Error *error)
{
FloatObject *obj = (FloatObject*) Heap_Malloc(heap, &t_float, error);
if(obj == 0)
return 0;
obj->val = val;
return (Object*) obj;
}
+41
View File
@@ -0,0 +1,41 @@
#include <assert.h>
#include <string.h>
#include "objects.h"
static long long int to_int(Object *obj, Error *err);
typedef struct {
Object base;
long long int val;
} IntObject;
static const Type t_int = {
.base = (Object) { .type = &t_type, .flags = Object_STATIC },
.name = "int",
.size = sizeof (IntObject),
.atomic = ATMTP_INT,
.to_int = to_int,
};
static long long int to_int(Object *obj, Error *err)
{
assert(obj);
assert(err);
assert(Object_GetType(obj) == &t_int);
(void) err;
return ((IntObject*) obj)->val;
}
Object *Object_FromInt(long long int val, Heap *heap, Error *error)
{
IntObject *obj = (IntObject*) Heap_Malloc(heap, &t_int, error);
if(obj == 0)
return 0;
obj->val = val;
return (Object*) obj;
}
+261
View File
@@ -0,0 +1,261 @@
#include <assert.h>
#include "../utils/defs.h"
#include "objects.h"
typedef struct {
Object base;
int mapper_size, count;
int *mapper;
Object **keys;
Object **vals;
} MapObject;
static Object *select(Object *self, Object *key, Heap *heap, Error *err);
static _Bool insert(Object *self, Object *key, Object *val, Heap *heap, Error *err);
static int count(Object *self);
static const Type t_map = {
.base = (Object) { .type = &t_type, .flags = Object_STATIC },
.name = "map",
.size = sizeof (MapObject),
.select = select,
.insert = insert,
.count = count,
};
static inline int calc_capacity(int mapper_size)
{
return mapper_size * 2.0 / 3.0;
}
Object *Object_NewMap(int num, Heap *heap, Error *error)
{
// Handle default args.
if(num < 0)
num = 0;
// Calculate initial mapper size.
int mapper_size, capacity;
{
mapper_size = 8;
while(calc_capacity(mapper_size) < num)
mapper_size <<= 1;
capacity = calc_capacity(mapper_size);
}
// Make the thing.
MapObject *obj = (MapObject*) Heap_Malloc(heap, &t_map, error);
{
if(obj == 0)
return 0;
obj->mapper_size = mapper_size;
obj->count = 0;
obj->mapper = Heap_RawMalloc(heap, sizeof(int) * capacity, error);
obj->keys = Heap_RawMalloc(heap, sizeof(Object*) * capacity, error);
obj->vals = Heap_RawMalloc(heap, sizeof(Object*) * capacity, error);
if(obj->mapper == NULL || obj->keys == NULL || obj->vals == NULL)
return NULL;
}
for(int i = 0; i < mapper_size; i += 1)
obj->mapper[i] = -1;
return (Object*) obj;
}
static Object *select(Object *self, Object *key, Heap *heap, Error *error)
{
assert(self != NULL);
assert(self->type == &t_map);
assert(key != NULL);
assert(heap != NULL);
assert(error != NULL);
MapObject *map = (MapObject*) self;
int mask = map->mapper_size - 1;
int hash = Object_Hash(key, error);
int pert = hash;
if(error->occurred)
// No hash function.
return 0;
int i = hash & mask;
while(1)
{
int k = map->mapper[i];
if(k == -1)
{
// Empty slot.
// This key is not present.
return NULL;
}
else
{
// Found an item.
// Is it the right one?
assert(k >= 0);
if(Object_Compare(key, map->keys[i], error))
// Found it!
return map->vals[i];
if(error->occurred)
// Key doesn't implement compare.
return 0;
// Not the one we wanted.
}
pert >>= 5;
i = (i * 5 + pert + 1) & mask;
}
UNREACHABLE;
return NULL;
}
static _Bool grow(MapObject *map, Heap *heap, Error *error)
{
assert(map != NULL);
int new_mapper_size = map->mapper_size << 1;
int new_capacity = calc_capacity(new_mapper_size);
int *mapper = Heap_RawMalloc(heap, sizeof(int) * new_mapper_size, error);
Object **keys = Heap_RawMalloc(heap, sizeof(Object*) * new_capacity, error);
Object **vals = Heap_RawMalloc(heap, sizeof(Object*) * new_capacity, error);
if(mapper == NULL || keys == NULL || vals == NULL)
return 0;
for(int i = 0; i < map->count; i += 1)
{
keys[i] = map->keys[i];
vals[i] = map->vals[i];
}
for(int i = 0; i < new_mapper_size; i += 1)
mapper[i] = -1;
// Rehash everything.
for(int i = 0; i < map->count; i += 1)
{
// This won't trigger an error because the key
// surely has a hash method since we already
// hashed it once.
int hash = Object_Hash(keys[i], error);
assert(error->occurred == 0);
int mask = new_mapper_size - 1;
int pert = hash;
int j = hash & mask;
while(1)
{
if(mapper[j] == -1)
{
// No collision.
// Insert here.
mapper[j] = i;
break;
}
// Collided. Find a new place.
pert >>= 5;
j = (j * 5 + pert + 1) & mask;
}
}
// Done.
map->mapper = mapper;
map->mapper_size = new_mapper_size;
map->keys = keys;
map->vals = vals;
return 1;
}
static _Bool insert(Object *self, Object *key, Object *val, Heap *heap, Error *error)
{
assert(error != NULL);
assert(key != NULL);
assert(val != NULL);
assert(heap != NULL);
assert(self != NULL);
assert(self->type == &t_map);
MapObject *map = (MapObject*) self;
if(map->count == calc_capacity(map->mapper_size))
if(!grow(map, heap, error))
return 0;
int mask = map->mapper_size - 1;
int hash = Object_Hash(key, error);
int pert = hash;
if(error->occurred)
// No hash function.
return 0;
int i = hash & mask;
while(1)
{
int k = map->mapper[i];
if(k == -1)
{
// Empty slot. We can insert it here.
Object *key_copy = Object_Copy(key, heap, error);
if(key_copy == NULL)
return NULL;
map->mapper[i] = map->count;
map->keys[map->count] = key_copy;
map->vals[map->count] = val;
map->count += 1;
return 1;
}
else
{
assert(k >= 0);
if(Object_Compare(key, map->keys[i], error))
{
// Already inserted.
// Overwrite the value.
map->vals[i] = val;
return 1;
}
if(error->occurred)
// Key doesn't implement compare.
return 0;
// Collision.
}
pert >>= 5;
i = (i * 5 + pert + 1) & mask;
}
UNREACHABLE;
return 0;
}
static int count(Object *self)
{
MapObject *map = (MapObject*) self;
return map->count;
}
+21
View File
@@ -0,0 +1,21 @@
#include <assert.h>
#include <string.h>
#include "objects.h"
static const Type t_none = {
.base = (Object) { .type = &t_type, .flags = Object_STATIC },
.name = "none",
.size = sizeof (Object),
};
static Object the_none_object = {
.type = &t_none,
.flags = Object_STATIC,
};
Object *Object_NewNone(Heap *heap, Error *error)
{
(void) heap;
(void) error;
return &the_none_object;
}
+69
View File
@@ -0,0 +1,69 @@
#include <string.h>
#include <assert.h>
#include "../utils/defs.h"
#include "../utils/hash.h"
#include "objects.h"
typedef struct {
Object base;
int size;
char *body;
} StringObject;
static int hash(Object *self);
static int count(Object *self);
static const Type t_string = {
.base = (Object) { .type = &t_type, .flags = Object_STATIC },
.name = "string",
.size = sizeof (StringObject),
.hash = hash,
.count = count,
};
Object *Object_FromString(const char *str, int len, Heap *heap, Error *error)
{
assert(str != NULL);
assert(heap != NULL);
assert(error != NULL);
if(len < 0)
len = strlen(str);
StringObject *strobj = Heap_Malloc(heap, &t_string, error);
if(strobj == NULL)
return NULL;
strobj->body = Heap_RawMalloc(heap, len+1, error);
strobj->size = len;
if(strobj->body == NULL)
return NULL;
memcpy(strobj->body, str, len);
strobj->body[len] = '\0';
return (Object*) strobj;
}
static int count(Object *self)
{
assert(self != NULL);
assert(self->type == &t_string);
StringObject *strobj = (StringObject*) self;
return strobj->size;
}
static int hash(Object *self)
{
assert(self != NULL);
assert(self->type == &t_string);
StringObject *strobj = (StringObject*) self;
return hashbytes((unsigned char*) strobj->body, strobj->size);
}
+286
View File
@@ -0,0 +1,286 @@
#include <assert.h>
#include "../utils/defs.h"
#include "objects.h"
const Type t_type = {
.base = (Object) { .type = &t_type, .flags = Object_STATIC },
.name = "type",
.size = sizeof (Type),
};
const char *Object_GetName(const Object *obj)
{
assert(obj);
const Type *type = Object_GetType(obj);
assert(type);
const char *name = type->name;
assert(name);
return name;
}
const Type *Object_GetType(const Object *obj)
{
assert(obj);
assert(obj->type);
return obj->type;
}
unsigned int Object_GetSize(const Object *obj, Error *err)
{
assert(err);
assert(obj);
const Type *type = Object_GetType(obj);
assert(type);
return type->size;
}
unsigned int Object_GetDeepSize(const Object *obj, Error *err)
{
assert(err);
assert(obj);
const Type *type = Object_GetType(obj);
assert(type);
if(type->deepsize == NULL)
{
Error_Report(err, "Object %s doesn't implement %s", Object_GetName(obj), __func__);
return 0;
}
return type->deepsize(obj);
}
int Object_Hash(Object *obj, Error *err)
{
assert(obj);
const Type *type = Object_GetType(obj);
assert(type);
if(type->hash == NULL)
{
Error_Report(err, "Object %s doesn't implement %s", Object_GetName(obj), __func__);
return -1;
}
return type->hash(obj);
}
Object *Object_Copy(Object *obj, Heap *heap, Error *err)
{
assert(err);
assert(obj);
const Type *type = Object_GetType(obj);
assert(type);
if(type->copy == NULL)
{
Error_Report(err, "Object %s doesn't implement %s", Object_GetName(obj), __func__);
return NULL;
}
return type->copy(obj, heap, err);
}
Object *Object_Call(Object *obj, Object **argv, unsigned int argc, Heap *heap, Error *err)
{
assert(err);
assert(obj);
const Type *type = Object_GetType(obj);
assert(type);
if(type->call == NULL)
{
Error_Report(err, "Object %s doesn't implement %s", Object_GetName(obj), __func__);
return NULL;
}
return type->call(obj, argv, argc, heap, err);
}
Object *Object_Select(Object *coll, Object *key, Heap *heap, Error *err)
{
assert(err);
assert(key);
assert(coll);
const Type *type = Object_GetType(coll);
assert(type);
if(type->select == NULL)
{
Error_Report(err, "Object %s doesn't implement %s", Object_GetName(coll), __func__);
return NULL;
}
return type->select(coll, key, heap, err);
}
Object *Object_Delete(Object *coll, Object *key, Heap *heap, Error *err)
{
assert(err);
assert(key);
assert(coll);
const Type *type = Object_GetType(coll);
assert(type);
if(type->delete == NULL)
{
Error_Report(err, "Object %s doesn't implement %s", Object_GetName(coll), __func__);
return NULL;
}
return type->delete(coll, key, heap, err);
}
_Bool Object_Insert(Object *coll, Object *key, Object *val, Heap *heap, Error *err)
{
assert(err);
assert(key);
assert(coll);
const Type *type = Object_GetType(coll);
assert(type);
if(type->insert == NULL)
{
Error_Report(err, "Object %s doesn't implement %s", Object_GetName(coll), __func__);
return 0;
}
return type->insert(coll, key, val, heap, err);
}
int Object_Count(Object *coll, Error *err)
{
assert(err);
assert(coll);
const Type *type = Object_GetType(coll);
assert(type);
if(type->count == NULL)
{
Error_Report(err, "Object %s doesn't implement %s", Object_GetName(coll), __func__);
return -1;
}
return type->count(coll);
}
Object *Object_Next(Object *iter, Heap *heap, Error *err)
{
assert(err);
assert(iter);
const Type *type = Object_GetType(iter);
assert(type);
if(type->next == NULL)
{
Error_Report(err, "Object %s doesn't implement %s", Object_GetName(iter), __func__);
return NULL;
}
return type->next(iter, heap, err);
}
Object *Object_Prev(Object *iter, Heap *heap, Error *err)
{
assert(err);
assert(iter);
const Type *type = Object_GetType(iter);
assert(type);
if(type->prev == NULL)
{
Error_Report(err, "Object %s doesn't implement %s", Object_GetName(iter), __func__);
return NULL;
}
return type->prev(iter, heap, err);
}
_Bool Object_IsInt(Object *obj)
{
assert(obj != NULL);
assert(obj->type != NULL);
return obj->type->atomic == ATMTP_INT;
}
_Bool Object_IsFloat(Object *obj)
{
assert(obj != NULL);
assert(obj->type != NULL);
return obj->type->atomic == ATMTP_FLOAT;
}
long long int Object_ToInt(Object *obj, Error *err)
{
assert(err);
assert(obj);
if(!Object_IsInt(obj))
{
Error_Report(err, 0, "Object is not an integer");
return 0;
}
const Type *type = Object_GetType(obj);
assert(type);
if(type->to_int == NULL)
{
Error_Report(err, "Object %s doesn't implement %s", Object_GetName(obj), __func__);
return 0;
}
return type->to_int(obj, err);
}
double Object_ToFloat(Object *obj, Error *err)
{
assert(err);
assert(obj);
if(!Object_IsFloat(obj))
{
Error_Report(err, 0, "Object is not a floating");
return 0;
}
const Type *type = Object_GetType(obj);
assert(type);
if(type->to_float == NULL)
{
Error_Report(err, "Object %s doesn't implement %s", Object_GetName(obj), __func__);
return 0;
}
return type->to_float(obj, err);
}
_Bool Object_Compare(Object *obj1, Object *obj2, Error *error)
{
if(obj1->type != obj2->type)
return 0;
if(obj1->type->op_eql == NULL)
{
Error_Report(error, "Object %s doesn't implement %s", Object_GetName(obj1), __func__);
return 0;
}
return obj1->type->op_eql(obj1, obj2);
}
+96
View File
@@ -0,0 +1,96 @@
#ifndef OBJECT_H
#define OBJECT_H
#include "../utils/error.h"
typedef struct Type Type;
typedef struct Object Object;
typedef struct xHeap Heap;
struct Object {
const Type *type;
unsigned int flags;
};
typedef enum {
ATMTP_NOTATOMIC = 0,
ATMTP_INT,
ATMTP_FLOAT,
} AtomicType;
struct Type {
Object base;
// Any.
const char *name;
unsigned int size;
AtomicType atomic;
_Bool (*init)(Object *self, Error *err);
_Bool (*free)(Object *self, Error *err);
int (*hash)(Object *self);
Object* (*copy)(Object *self, Heap *heap, Error *err);
Object* (*call)(Object *self, Object **argv, unsigned int argc, Heap *heap, Error *err);
unsigned int (*deepsize)(const Object *self);
// Collections.
Object *(*select)(Object *self, Object *key, Heap *heap, Error *err);
Object *(*delete)(Object *self, Object *key, Heap *heap, Error *err);
_Bool (*insert)(Object *self, Object *key, Object *val, Heap *heap, Error *err);
int (*count)(Object *self);
// Iterators.
Object *(*next)(Object *self, Heap *heap, Error *err);
Object *(*prev)(Object *self, Heap *heap, Error *err);
// Some.
union {
long long int (*to_int)(Object *self, Error *err);
double (*to_float)(Object *self, Error *err);
char *(*to_string)(Object *self, int *size, Heap *heap, Error *err);
};
_Bool (*op_eql)(Object *self, Object *other);
};
enum {
Object_STATIC = 1,
};
Heap* Heap_New(int size);
void Heap_Free(Heap *heap);
void* Heap_Malloc (Heap *heap, const Type *type, Error *err);
void* Heap_RawMalloc(Heap *heap, int size, Error *err);
const Type* Object_GetType(const Object *obj);
const char* Object_GetName(const Object *obj);
unsigned int Object_GetSize(const Object *obj, Error *err);
unsigned int Object_GetDeepSize(const Object *obj, Error *err);
int Object_Hash (Object *obj, Error *err);
Object* Object_Copy (Object *obj, Heap *heap, Error *err);
Object* Object_Call (Object *obj, Object **argv, unsigned int argc, Heap *heap, Error *err);
Object* Object_Select(Object *coll, Object *key, Heap *heap, Error *err);
Object* Object_Delete(Object *coll, Object *key, Heap *heap, Error *err);
_Bool Object_Insert(Object *coll, Object *key, Object *val, Heap *heap, Error *err);
int Object_Count (Object *coll, Error *err);
Object* Object_Next (Object *iter, Heap *heap, Error *err);
Object* Object_Prev (Object *iter, Heap *heap, Error *err);
Object* Object_NewMap(int num, Heap *heap, Error *error);
Object* Object_NewNone(Heap *heap, Error *error);
Object* Object_FromInt (long long int val, Heap *heap, Error *error);
Object* Object_FromFloat (double val, Heap *heap, Error *error);
Object* Object_FromString(const char *str, int len, Heap *heap, Error *error);
_Bool Object_IsInt (Object *obj);
_Bool Object_IsFloat(Object *obj);
long long int Object_ToInt (Object *obj, Error *err);
double Object_ToFloat(Object *obj, Error *err);
_Bool Object_Compare(Object *obj1, Object *obj2, Error *error);
extern const Type t_type;
#endif
+53
View File
@@ -0,0 +1,53 @@
#include <assert.h>
#include "../utils/defs.h"
#include "../objects/objects.h"
#include "runtime.h"
typedef struct {
Object base;
Runtime *runtime;
Executable *exe;
int index;
} FunctionObject;
static Object *call(Object *self, Object **argv, unsigned int argc, Heap *heap, Error *error);
static const Type t_func = {
.base = (Object) { .type = &t_type, .flags = Object_STATIC },
.name = "function",
.size = sizeof (FunctionObject),
.call = call,
};
static Object *call(Object *self, Object **argv, unsigned int argc, Heap *heap, Error *error)
{
assert(self != NULL);
assert(heap != NULL);
assert(error != NULL);
FunctionObject *func = (FunctionObject*) self;
assert(func->exe != NULL);
assert(func->index >= 0);
return run(func->runtime, error, func->exe, func->index, argv, argc);
}
Object *Object_FromNojaFunction(Runtime *runtime, Executable *exe, int index, Heap *heap, Error *error)
{
assert(runtime != NULL);
assert(exe != NULL);
assert(index >= 0);
assert(heap != NULL);
assert(error != NULL);
FunctionObject *func = (FunctionObject*) Heap_Malloc(heap, &t_func, error);
if(func == NULL)
return NULL;
func->runtime = runtime;
func->exe = exe;
func->index = index;
return (Object*) func;
}
+522
View File
@@ -0,0 +1,522 @@
#include <stdlib.h>
#include "../utils/defs.h"
#include "../utils/stack.h"
#include "runtime.h"
#define MAX_FRAME_STACK 16
typedef struct Frame Frame;
struct Frame {
Frame *prev;
Object *vars;
Executable *exe;
int index, used;
};
struct xRuntime {
int depth;
Frame *frame;
Stack *stack;
Heap *heap;
};
Runtime *Runtime_New(int stack_size, int heap_size)
{
if(stack_size < 0)
stack_size = 1024;
if(heap_size < 0)
heap_size = 65536;
Runtime *runtime;
{
runtime = malloc(sizeof(Runtime));
if(runtime == NULL)
return NULL;
runtime->heap = Heap_New(heap_size);
if(runtime->heap == NULL)
{
free(runtime);
return NULL;
}
runtime->stack = Stack_New(stack_size);
if(runtime->stack == NULL)
{
Heap_Free(runtime->heap);
free(runtime);
}
runtime->frame = NULL;
runtime->depth = 0;
}
return runtime;
}
void Runtime_Free(Runtime *runtime)
{
Heap_Free(runtime->heap);
Stack_Free(runtime->stack);
free(runtime);
}
_Bool Runtime_Push(Runtime *runtime, Error *error, Object *obj)
{
assert(runtime != NULL);
assert(error != NULL);
assert(obj != NULL);
if(runtime->depth == 0)
{
Error_Report(error, 0, "There are no frames on the stack");
return 0;
}
assert(runtime->frame->used <= MAX_FRAME_STACK);
if(runtime->frame->used == MAX_FRAME_STACK)
{
Error_Report(error, 0, "Frames stack limit of %d reached", MAX_FRAME_STACK);
return 0;
}
if(!Stack_Push(runtime->stack, obj))
{
Error_Report(error, 0, "Out of stack");
return 0;
}
runtime->frame->used += 1;
return 1;
}
_Bool Runtime_Pop(Runtime *runtime, Error *error, unsigned int n)
{
assert(runtime != NULL);
assert(error != NULL);
if(runtime->depth == 0)
{
Error_Report(error, 0, "There are no frames on the stack");
return 0;
}
assert(runtime->frame->used >= 0);
if((unsigned int) runtime->frame->used < n)
{
Error_Report(error, 0, "Frames has not enough values on the stack");
return 0;
}
// The frame has something on the stack,
// this means that the stack isn't empty
// and popping won't fail.
(void) Stack_Pop(runtime->stack, n);
runtime->frame->used -= n;
assert(runtime->frame->used >= 0);
return 1;
}
typedef struct {
Executable *exe;
int index;
} SnapshotNode;
struct xSnapshot {
int depth;
SnapshotNode nodes[];
};
Snapshot *Snapshot_New(Runtime *runtime)
{
assert(runtime->depth >= 0);
Snapshot *snapshot = malloc(sizeof(Snapshot) + sizeof(SnapshotNode) * runtime->depth);
if(snapshot == NULL)
return NULL;
{
Frame *f = runtime->frame;
snapshot->depth = 0;
while(snapshot->depth < runtime->depth)
{
assert(f != NULL);
SnapshotNode *node = snapshot->nodes + snapshot->depth;
node->exe = Executable_Copy(f->exe);
node->index = f->index;
if(node->exe == NULL)
goto abort;
f = f->prev;
snapshot->depth += 1;
}
assert(f == NULL);
}
return snapshot;
abort:
Snapshot_Free(snapshot);
return NULL;
}
void Snapshot_Free(Snapshot *snapshot)
{
for(int i = 0; i < snapshot->depth; i += 1)
{
Executable *exe = snapshot->nodes[i].exe;
Executable_Free(exe);
}
free(snapshot);
}
void Snapshot_Print(Snapshot *snapshot, FILE *fp)
{
assert(snapshot != NULL);
assert(fp != NULL);
fprintf(fp, " (Snapshot can't be printed yet)\n");
}
static Object *do_math_op(Object *lop, Object *rop, Opcode opcode, Heap *heap, Error *error)
{
assert(lop != NULL);
assert(rop != NULL);
#define APPLY(x, y, z, id) \
switch(opcode) \
{ \
case OPCODE_ADD: (z) = (x) + (y); break; \
case OPCODE_SUB: (z) = (x) - (y); break; \
case OPCODE_MUL: (z) = (x) * (y); break; \
case OPCODE_DIV: (z) = (x) / (y); break; \
default: assert(0); break; \
}
Object *res;
if(Object_IsInt(lop))
{
long long int raw_lop = Object_ToInt(lop, error);
if(error->occurred)
return NULL;
if(Object_IsInt(rop))
{
// int + int
long long int raw_rop = Object_ToInt(rop, error);
if(error->occurred)
return NULL;
long long int raw_res;
APPLY(raw_lop, raw_rop, raw_res, id)
res = Object_FromInt(raw_res, heap, error);
}
else if(Object_IsFloat(rop))
{
// int + float
double raw_rop = Object_ToFloat(rop, error);
if(error->occurred)
return NULL;
double raw_res;
APPLY((double) raw_lop, raw_rop, raw_res, id)
res = Object_FromFloat(raw_res, heap, error);
}
else
{
Error_Report(error, 0, "Arithmetic operation on a non-numeric object");
return NULL;
}
}
else if(Object_IsFloat(lop))
{
double raw_lop = Object_ToFloat(lop, error);
if(error->occurred)
return NULL;
if(Object_IsInt(rop))
{
// float + int
long long int raw_rop = Object_ToInt(rop, error);
if(error->occurred)
return NULL;
double raw_res;
APPLY(raw_lop, (double) raw_rop, raw_res, id)
res = Object_FromFloat(raw_res, heap, error);
}
else if(Object_IsFloat(rop))
{
// float + float
double raw_rop = Object_ToFloat(rop, error);
if(error->occurred)
return NULL;
double raw_res;
APPLY(raw_lop, raw_rop, raw_res, id)
res = Object_FromFloat(raw_res, heap, error);
}
else
{
Error_Report(error, 0, "Arithmetic operation on a non-numeric object");
return NULL;
}
}
else
{
Error_Report(error, 0, "Arithmetic operation on a non-numeric object");
return NULL;
}
#undef APPLY
return res;
}
static _Bool step(Runtime *runtime, Error *error)
{
assert(runtime != NULL);
Opcode opcode;
Operand ops[3];
int opc = sizeof(ops) / sizeof(ops[0]);
if(!Executable_Fetch(runtime->frame->exe, runtime->frame->index, &opcode, ops, &opc))
{
Error_Report(error, 1, "Invalid instruction index");
return 0;
}
runtime->frame->index += 1;
switch(opcode)
{
case OPCODE_NOPE:
// Do nothing.
return 1;
case OPCODE_POS:
Error_Report(error, 1, "POS not implemented");
return 0;
case OPCODE_NEG:
Error_Report(error, 1, "NEG not implemented");
return 0;
case OPCODE_ADD:
case OPCODE_SUB:
case OPCODE_MUL:
case OPCODE_DIV:
{
Object *rop = Stack_Top(runtime->stack, 0);
Object *lop = Stack_Top(runtime->stack, -1);
if(!Runtime_Pop(runtime, error, 2))
return 0;
// We managed to pop rop and lop,
// so we know they're not NULL.
assert(rop != NULL);
assert(lop != NULL);
Object *res = do_math_op(lop, rop, opcode, runtime->heap, error);
if(res == NULL)
return 0;
if(!Runtime_Push(runtime, error, res))
return 0;
break;
}
case OPCODE_PUSHI:
{
assert(opc == 1);
assert(ops[0].type == OPTP_INT);
Object *obj = Object_FromInt(ops[0].as_int, runtime->heap, error);
if(obj == NULL)
return 0;
if(!Runtime_Push(runtime, error, obj))
return 0;
return 1;
}
case OPCODE_PUSHF:
{
assert(opc == 1);
assert(ops[0].type == OPTP_FLOAT);
Object *obj = Object_FromFloat(ops[0].as_float, runtime->heap, error);
if(obj == NULL)
return 0;
if(!Runtime_Push(runtime, error, obj))
return 0;
return 1;
}
case OPCODE_PUSHS:
{
assert(opc == 1);
assert(ops[0].type == OPTP_STRING);
Object *obj = Object_FromString(ops[0].as_string, -1, runtime->heap, error);
if(obj == NULL)
return 0;
if(!Runtime_Push(runtime, error, obj))
return 0;
return 1;
}
case OPCODE_PUSHV:
{
assert(opc == 1);
assert(ops[0].type == OPTP_STRING);
Object *key = Object_FromString(ops[0].as_string, -1, runtime->heap, error);
if(key == NULL)
return 0;
Object *obj = Object_Select(runtime->frame->vars, key, runtime->heap, error);
if(obj == NULL)
{
if(error->occurred == 0)
// There's no such variable.
Error_Report(error, 1, "Reference to undefined variable \"%s\"", ops[0].as_string);
return 0;
}
return 1;
}
case OPCODE_RETURN:
return 0;
default:
UNREACHABLE;
return 0;
}
return 1;
}
Object *run(Runtime *runtime, Error *error, Executable *exe, int index, Object **argv, int argc)
{
assert(runtime != NULL);
assert(error != NULL);
assert(exe != NULL);
assert(index >= 0);
assert(argc >= 0);
// Initialize the frame.
Frame frame;
{
frame.prev = NULL;
frame.vars = Object_NewMap(-1, runtime->heap, error);
frame.exe = Executable_Copy(exe);
frame.index = index;
frame.used = 0;
if(frame.vars == NULL)
return NULL;
if(frame.exe == NULL)
{
Error_Report(error, 1, "Failed to copy executable");
return NULL;
}
// Add the frame to the runtime.
frame.prev = runtime->frame;
runtime->frame = &frame;
runtime->depth += 1;
}
// Push the initial values of the frame.
for(int i = 0; i < argc; i += 1)
if(!Runtime_Push(runtime, error, argv[i]))
goto cleanup;
// Run the code.
while(step(runtime, error));
// This is what the function will return.
Object *result = NULL;
// If an error occurred, we want to return NULL.
if(error->occurred == 0)
{
// If the step function left something
// on the stack, we return that. If it
// didn't, we return some other default
// value like "none".
if(frame.used == 0)
{
// Nothing to return on the stack. Set to none.
result = Object_NewNone(runtime->heap, error);
}
else
{
result = Stack_Top(runtime->stack, 0);
assert(result != NULL);
}
}
cleanup:
// Remove the frame-owned items from the stack.
// This can't fail.
(void) Stack_Pop(runtime->stack, frame.used);
// Deinitialize the frame.
{
// Remove the frame from the runtime.
runtime->frame = runtime->frame->prev;
runtime->depth -= 1;
// Deallocate the fields.
Executable_Free(frame.exe);
}
return result;
}
+22
View File
@@ -0,0 +1,22 @@
#ifndef RUNTIME_H
#define RUNTIME_H
#include <stdio.h> // meh.. just for the definition of FILE.
#include "../utils/error.h"
#include "../objects/objects.h"
#include "../common/executable.h"
typedef struct xRuntime Runtime;
Runtime* Runtime_New(int stack_size, int heap_size);
void Runtime_Free(Runtime *runtime);
_Bool Runtime_Pop(Runtime *runtime, Error *error, unsigned int n);
_Bool Runtime_Push(Runtime *runtime, Error *error, Object *obj);
typedef struct xSnapshot Snapshot;
Snapshot *Snapshot_New(Runtime *runtime);
void Snapshot_Free(Snapshot *snapshot);
void Snapshot_Print(Snapshot *snapshot, FILE *fp);
Object *run(Runtime *runtime, Error *error, Executable *exe, int index, Object **argv, int argc);
Object *Object_FromNojaFunction(Runtime *runtime, Executable *exe, int offset, Heap *heap, Error *error);
#endif
+29
View File
@@ -0,0 +1,29 @@
#include <assert.h>
#include "runtime_error.h"
static void on_report(Error *error)
{
assert(error != NULL);
RuntimeError *error2 = (RuntimeError*) error;
if(error2->runtime != NULL)
error2->snapshot = Snapshot_New(error2->runtime);
}
void RuntimeError_Init(RuntimeError *error, Runtime *runtime)
{
assert(error != NULL);
Error_Init2(&error->base, on_report);
error->runtime = runtime;
error->snapshot = NULL;
}
void RuntimeError_Free(RuntimeError *error)
{
if(error->snapshot)
Snapshot_Free(error->snapshot);
Error_Free(&error->base);
}
+14
View File
@@ -0,0 +1,14 @@
#ifndef RUNTIME_ERROR_H
#define RUNTIME_ERROR_H
#include "../utils/error.h"
#include "runtime.h"
typedef struct {
Error base;
Runtime *runtime;
Snapshot *snapshot;
} RuntimeError;
void RuntimeError_Init(RuntimeError *error, Runtime *runtime);
void RuntimeError_Free(RuntimeError *error);
#endif
+217
View File
@@ -0,0 +1,217 @@
#include <stdint.h>
#include <assert.h>
#include <stdlib.h>
#include "defs.h"
#include "bpalloc.h"
#if USING_VALGRIND
#include <valgrind/memcheck.h>
#endif
#define CHUNK_SIZE 4096
#define PADDING 8
typedef struct BPAllocChunk BPAllocChunk;
struct BPAllocChunk {
BPAllocChunk *prev;
char body[];
};
struct xBPAlloc {
int flags;
void *userp;
void *(*fn_malloc)(void *userp, int size);
void (*fn_free )(void *userp, void *addr);
int minsize, size, used;
BPAllocChunk *tail;
};
enum {
FG_STATIC = 1,
};
static void *default_fn_malloc(void *userp, int size);
static void default_fn_free (void *userp, void *addr);
BPAlloc *BPAlloc_Init(int chunk_size)
{
return BPAlloc_Init2(-1, chunk_size, NULL, NULL, NULL);
}
BPAlloc *BPAlloc_Init2(int first_size, int chunk_size,
void *userp,
void *(*fn_malloc)(void *userp, int size),
void (*fn_free )(void *userp, void *addr))
{
if(chunk_size < 0)
chunk_size = CHUNK_SIZE;
if(first_size < 0)
first_size = chunk_size;
if(fn_malloc == NULL)
{
userp = NULL;
fn_malloc = default_fn_malloc;
fn_free = default_fn_free;
}
void *temp = fn_malloc(userp, sizeof(BPAlloc) + sizeof(BPAllocChunk) + first_size + PADDING);
if(temp == NULL)
return NULL;
BPAlloc *alloc = temp;
BPAllocChunk *chunk = (BPAllocChunk*) (alloc + 1);
chunk->prev = NULL;
alloc->flags = 0;
alloc->used = 0;
alloc->size = first_size;
alloc->tail = chunk;
alloc->minsize = chunk_size;
alloc->userp = userp;
alloc->fn_malloc = fn_malloc;
alloc->fn_free = fn_free;
#if USING_VALGRIND
VALGRIND_CREATE_MEMPOOL(alloc, PADDING, 0);
#endif
return alloc;
}
BPAlloc *BPAlloc_Init3(void *mem, int mem_size, int chunk_size,
void *userp,
void *(*fn_malloc)(void *userp, int size),
void (*fn_free )(void *userp, void *addr))
{
assert(mem != NULL);
assert(mem_size >= 0);
if(chunk_size < 0)
chunk_size = CHUNK_SIZE;
if(fn_malloc == NULL)
{
userp = NULL;
fn_malloc = default_fn_malloc;
fn_free = default_fn_free;
}
int required = sizeof(BPAlloc)
+ sizeof(BPAllocChunk)
+ PADDING;
if(mem_size < required)
// Not enough memory was provided.
return NULL;
BPAlloc *alloc = mem;
BPAllocChunk *chunk = (BPAllocChunk*) (alloc + 1);
chunk->prev = NULL;
alloc->flags = FG_STATIC;
alloc->used = 0;
alloc->size = mem_size - sizeof(BPAlloc) - sizeof(BPAllocChunk);
alloc->tail = chunk;
alloc->minsize = chunk_size;
alloc->userp = userp;
alloc->fn_malloc = fn_malloc;
alloc->fn_free = fn_free;
#if USING_VALGRIND
VALGRIND_CREATE_MEMPOOL(alloc, PADDING, 0);
#endif
return alloc;
}
void BPAlloc_Free(BPAlloc *alloc)
{
assert(alloc != NULL);
#if USING_VALGRIND
VALGRIND_DESTROY_MEMPOOL(alloc);
#endif
BPAllocChunk *chunk = alloc->tail;
while(chunk->prev)
{
BPAllocChunk *prev = chunk->prev;
if(alloc->fn_free)
alloc->fn_free(alloc->userp, chunk);
chunk = prev;
}
if(!(alloc->flags & FG_STATIC))
if(alloc->fn_free)
alloc->fn_free(alloc->userp, alloc);
}
void *BPAlloc_Malloc(BPAlloc *alloc, int req_size)
{
assert(alloc != NULL);
assert(req_size >= 0);
alloc->used += PADDING;
if(alloc->used & 7)
alloc->used = (alloc->used & ~7) + 8;
if(alloc->used + req_size > alloc->size)
{
// If the chunk size is lower than the
// requested size, then set the chunk
// size to the requested size.
int chunk_size = MAX(alloc->minsize, req_size + PADDING);
assert(alloc->fn_malloc != NULL);
BPAllocChunk *chunk = alloc->fn_malloc(alloc->userp, sizeof(BPAllocChunk) + chunk_size);
if(chunk == NULL)
return NULL;
chunk->prev = alloc->tail;
alloc->tail = chunk;
alloc->size = chunk_size;
alloc->used = PADDING;
if(alloc->used & 7)
alloc->used = (alloc->used & ~7) + 8;
}
void *addr = alloc->tail->body + alloc->used;
assert(((intptr_t) addr) % 8 == 0);
alloc->used += req_size;
#if USING_VALGRIND
VALGRIND_MEMPOOL_ALLOC(alloc, addr, req_size);
// VALGRIND_MAKE_MEM_NOACCESS((char*) addr - PADDING, PADDING);
// VALGRIND_MAKE_MEM_NOACCESS((char*) addr + req_size, PADDING);
#endif
return addr;
}
static void *default_fn_malloc(void *userp, int size)
{
assert(userp == NULL);
assert(size >= 0);
return malloc(size);
}
static void default_fn_free(void *userp, void *addr)
{
assert(userp == NULL);
free(addr);
}
+9
View File
@@ -0,0 +1,9 @@
#ifndef BPALLOC_H
#define BPALLOC_H
typedef struct xBPAlloc BPAlloc;
BPAlloc* BPAlloc_Init(int chunk_size);
BPAlloc* BPAlloc_Init2(int first_size, int chunk_size, void *userp, void *(*fn_malloc)(void *userp, int size), void (*fn_free )(void *userp, void *addr));
BPAlloc* BPAlloc_Init3(void *mem, int mem_size, int chunk_size, void *userp, void *(*fn_malloc)(void *userp, int size), void (*fn_free )(void *userp, void *addr));
void BPAlloc_Free(BPAlloc *alloc);
void* BPAlloc_Malloc(BPAlloc *alloc, int req_size);
#endif
+177
View File
@@ -0,0 +1,177 @@
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "bucketlist.h"
#include "defs.h"
#define MIN_BUCKET_SIZE 4096
typedef struct Bucket Bucket;
struct Bucket {
Bucket* next;
int size,
used,
aidx;
char body[];
};
struct xBucketList {
BPAlloc *alloc;
Bucket *head,
*tail;
int size;
};
BucketList *BucketList_New(BPAlloc *alloc)
{
assert(alloc != NULL);
BucketList *blist = BPAlloc_Malloc(alloc, sizeof(BucketList) + sizeof(Bucket) + MIN_BUCKET_SIZE);
if(blist == NULL)
return NULL;
Bucket *head = (Bucket*) (blist + 1);
head->next = NULL;
head->size = MIN_BUCKET_SIZE;
head->used = 0;
head->aidx = 0;
blist->alloc = alloc;
blist->head = head;
blist->tail = head;
blist->size = 0;
return blist;
}
int BucketList_Size(BucketList *blist)
{
return blist->size;
}
static Bucket *make_bucket(BPAlloc *alloc, int size)
{
Bucket *new_bucket = BPAlloc_Malloc(alloc, sizeof(Bucket) + size);
if(new_bucket == NULL)
return NULL;
// Initialize it.
new_bucket->next = NULL;
new_bucket->size = size;
new_bucket->used = 0;
new_bucket->aidx = -1;
return new_bucket;
}
static void append_bucket(BucketList *blist, Bucket *new_bucket)
{
new_bucket->aidx = blist->size;
blist->tail->next = new_bucket;
blist->tail = new_bucket;
}
_Bool BucketList_Append(BucketList *blist, const void *data, int size)
{
assert(blist != NULL);
assert(size >= 0);
int not_copied_yet = size;
while(not_copied_yet > 0)
{
// Copy until there's nothing left
// or until the current bucket is
// full. If the bucket is already
// full, add another one.
int left_in_bucket = blist->tail->size - blist->tail->used;
if(left_in_bucket == 0)
{
Bucket *new_bucket = make_bucket(blist->alloc, MIN_BUCKET_SIZE);
if(new_bucket == NULL)
return 0;
append_bucket(blist, new_bucket);
}
// Decide how much to copy.
int copying = MIN(not_copied_yet, left_in_bucket);
// Copy into the bucket.
{
char *dst = blist->tail->body + blist->tail->used;
if(data == NULL)
memset(dst, 0, copying);
else
memcpy(dst, data + size - not_copied_yet, copying);
blist->tail->used += copying;
}
not_copied_yet -= copying;
}
blist->size += size;
return 1;
}
void *BucketList_Append2(BucketList *blist, const void *data, int size)
{
assert(blist != NULL);
assert(size >= 0);
// If the data doesn't fit inside the
// current bucket, add another one with
// enough space.
if(blist->tail->used + size > blist->tail->size)
{
int bucket_size = MAX(MIN_BUCKET_SIZE, size);
Bucket *new_bucket = make_bucket(blist->alloc, bucket_size);
if(new_bucket == NULL)
return 0;
append_bucket(blist, new_bucket);
}
void *addr = blist->tail->body + blist->tail->used;
// Do the copying.
if(data == NULL)
memset(addr, 0, size);
else
memcpy(addr, data, size);
blist->tail->used += size;
blist->size += size;
return addr;
}
void BucketList_Copy(BucketList *blist, void *dest, int len)
{
assert(blist != NULL);
assert(dest != NULL);
if(len < 0)
len = blist->size;
int copied = 0;
Bucket *bucket = blist->head;
while(bucket && copied < len)
{
int copying = MIN(len - copied, bucket->used);
assert(copying >= 0);
memcpy((char*) dest + copied, bucket->body, copying);
copied += copying;
bucket = bucket->next;
}
}
+10
View File
@@ -0,0 +1,10 @@
#ifndef BUCKETLIST_H
#define BUCKETLIST_H
#include "bpalloc.h"
typedef struct xBucketList BucketList;
BucketList *BucketList_New(BPAlloc *alloc);
int BucketList_Size(BucketList *blist);
void BucketList_Copy(BucketList *blist, void *dest, int len);
_Bool BucketList_Append (BucketList *blist, const void *data, int size);
void *BucketList_Append2(BucketList *blist, const void *data, int size);
#endif
+17
View File
@@ -0,0 +1,17 @@
#include <assert.h>
#ifndef MAX
#define MAX(x, y) ((x) > (y) ? (x) : (y))
#endif
#ifndef MIN
#define MIN(x, y) ((x) < (y) ? (x) : (y))
#endif
#ifndef NULL
#define NULL ((void*) 0)
#endif
#define UNREACHABLE assert(0);
#define membersizeof(type, member) (sizeof(((type*) 0)->member))
+88
View File
@@ -0,0 +1,88 @@
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <stdio.h>
#include "error.h"
void Error_Init(Error *err)
{
memset(err, 0, sizeof (Error));
}
void Error_Init2(Error *err, void (*on_report)(Error *err))
{
memset(err, 0, sizeof (Error));
err->on_report = on_report;
}
void Error_Free(Error *err)
{
if(err->message2 != err->message)
free(err->message);
memset(err, 0, sizeof (Error));
}
void _Error_Report(Error *err, _Bool internal,
const char *file, const char *func, int line,
const char *fmt, ...)
{
va_list va;
va_start(va, fmt);
_Error_Report2(err, internal, file, func, line, fmt, va);
va_end(va);
}
void _Error_Report2(Error *err, _Bool internal,
const char *file, const char *func, int line,
const char *fmt, va_list va)
{
assert(err);
assert(file);
assert(func);
assert(line > 0);
assert(fmt);
assert(err->occurred == 0);
err->occurred = 1;
err->internal = internal;
err->file = file;
err->func = func;
err->line = line;
va_list va2;
va_copy(va2, va);
int p = vsnprintf(err->message2, sizeof(err->message2), fmt, va);
assert(p > -1);
if((unsigned int) p > sizeof(err->message2)-1)
{
char *temp = malloc(p+1);
if(temp == NULL)
{
err->truncated = 1;
err->message = err->message2;
err->length = sizeof(err->message2)-1;
}
else
{
snprintf(temp, p+1, fmt, va2);
err->truncated = 0;
err->message = temp;
err->length = p;
}
}
else
{
err->truncated = 0;
err->message = err->message2;
err->length = p;
}
va_end(va2);
if(err->on_report)
err->on_report(err);
}
+26
View File
@@ -0,0 +1,26 @@
#ifndef ERROR_H
#define ERROR_H
#include <stdarg.h>
typedef struct Error Error;
struct Error {
void (*on_report)(Error *err);
_Bool occurred,
internal,
truncated;
int length;
char* message;
char message2[256];
const char *file,
*func;
int line;
};
void Error_Init(Error *err);
void Error_Init2(Error *err, void (*on_report)(Error *err));
void Error_Free(Error *err);
#define Error_Report(err, internal, fmt, ...) _Error_Report(err, internal, __FILE__, __func__, __LINE__, fmt, ## __VA_ARGS__)
void _Error_Report (Error *err, _Bool internal, const char *file, const char *func, int line, const char *fmt, ...);
void _Error_Report2(Error *err, _Bool internal, const char *file, const char *func, int line, const char *fmt, va_list va);
#endif
+19
View File
@@ -0,0 +1,19 @@
#include <stdint.h>
#include "hash.h"
int hashbytes(unsigned char *str, int len)
{
int x = (intptr_t) str; // just to not use 0.
x ^= *str << 7;
for(int i = 0; i < len; i += 1)
x = (1000003UL * x) ^ *str++;
x ^= len;
if(x == -1)
x = -2;
return x;
}
+4
View File
@@ -0,0 +1,4 @@
#ifndef HASH_H
#define HASH_H
int hashbytes(unsigned char *str, int len);
#endif
+106
View File
@@ -0,0 +1,106 @@
#include <assert.h>
#include <string.h>
#include "promise.h"
#include "defs.h"
typedef struct Gap Gap;
struct Gap {
Gap *next;
void *dest;
void *userp;
void (*callback)(void*);
};
struct xPromise {
BPAlloc *alloc;
_Bool set;
Gap *gaps;
int size;
char body[];
};
Promise *Promise_New(BPAlloc *alloc, int size)
{
assert(alloc != NULL);
assert(size >= 0);
Promise *promise = BPAlloc_Malloc(alloc, sizeof(Promise) + size);
if(promise == NULL)
return NULL;
promise->alloc = alloc;
promise->set = 0;
promise->gaps = NULL;
promise->size = size;
return promise;
}
unsigned int Promise_Size(Promise *promise)
{
return promise->size;
}
void Promise_Delete(Promise *promise)
{
assert(promise->set == 1);
}
void Promise_Resolve(Promise *promise, const void *data, int size)
{
assert(size >= 0);
assert(size == promise->size);
assert(promise->set == 0);
memcpy(promise->body, data, size);
promise->set = 1;
Gap *gap = promise->gaps;
while(gap)
{
memcpy(gap->dest, data, size);
if(gap->callback)
gap->callback(gap->userp);
gap = gap->next;
}
promise->gaps = NULL;
}
_Bool Promise_Subscribe(Promise *promise, void *dest)
{
assert(promise != NULL);
assert(dest != NULL);
return Promise_Subscribe2(promise, dest, NULL, NULL);
}
_Bool Promise_Subscribe2(Promise *promise, void *dest, void *userp, void (*callback)(void*))
{
assert(promise != NULL);
assert(dest != NULL);
if(promise->set == 0)
{
Gap *gap = BPAlloc_Malloc(promise->alloc, sizeof(Gap));
if(gap == NULL)
return 0;
gap->next = promise->gaps;
gap->dest = dest;
gap->userp = userp;
gap->callback = callback;
promise->gaps = gap;
}
else
{
memcpy(dest, promise->body, promise->size);
if(callback)
callback(userp);
}
return 1;
}
+11
View File
@@ -0,0 +1,11 @@
#ifndef PROMISE_H
#define PROMISE_H
#include "bpalloc.h"
typedef struct xPromise Promise;
Promise *Promise_New(BPAlloc *alloc, int size);
unsigned int Promise_Size(Promise *promise);
void Promise_Delete(Promise *promise);
void Promise_Resolve(Promise *promise, const void *data, int size);
_Bool Promise_Subscribe(Promise *promise, void *dest);
_Bool Promise_Subscribe2(Promise *promise, void *dest, void *userp, void (*callback)(void*));
#endif
+160
View File
@@ -0,0 +1,160 @@
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include "source.h"
struct xSource {
char *name;
char *body;
int size;
int refs;
};
Source *Source_Copy(Source *s)
{
s->refs += 1;
return s;
}
void Source_Free(Source *s)
{
s->refs -= 1;
assert(s->refs >= 0);
if(s->refs == 0)
free(s);
}
const char *Source_GetName(const Source *s)
{
return s->name;
}
const char *Source_GetBody(const Source *s)
{
return s->body;
}
unsigned int Source_GetSize(const Source *s)
{
return s->size;
}
Source *Source_FromFile(const char *file, Error *error)
{
assert(file != NULL);
// Open the file and get it's size.
// at the end of the block, the file
// cursor will point at the start of
// the file.
FILE *fp;
int size;
{
fp = fopen(file, "rb");
if(fp == NULL)
{
if(errno == ENOENT)
Error_Report(error, 0, "File \"%s\" doesn't exist", file);
else
Error_Report(error, 1, "Call to fopen failed (%s, errno = %d)", strerror(errno), errno);
return NULL;
}
if(fseek(fp, 0, SEEK_END))
{
Error_Report(error, 1, "Call to fseek failed (%s, errno = %d)", strerror(errno), errno);
fclose(fp);
return NULL;
}
size = ftell(fp);
if(size < 0)
{
Error_Report(error, 1, "Call to ftell failed (%s, errno = %d)", strerror(errno), errno);
fclose(fp);
return NULL;
}
if(fseek(fp, 0, SEEK_END))
{
Error_Report(error, 1, "Call to fseek failed (%s, errno = %d)", strerror(errno), errno);
fclose(fp);
return NULL;
}
}
// Allocate the source structure.
Source *s;
{
int namel = strlen(file);
s = malloc(sizeof(Source) + namel + size + 2);
if(s == NULL)
{
Error_Report(error, 1, "No memory");
fclose(fp);
return NULL;
}
s->name = (char*) (s + 1);
s->body = s->name + namel + 1;
}
// Copy the name into it.
strcpy(s->name, file);
s->size = size;
s->refs = 1;
// Now copy the file contents into it.
{
int p = fread(s->body, 1, size, fp);
if(p != size)
{
Error_Report(error, 1, "Call to fread failed, %d bytes out of %d were read (%s, errno = %d)", p, size, strerror(errno), errno);
fclose(fp);
free(s);
return NULL;
}
s->body[s->size] = '\0';
}
fclose(fp);
return s;
}
Source *Source_FromString(const char *name, const char *body, int size, Error *error)
{
assert(body != NULL);
if(size < 0)
size = strlen(body);
int namel = name ? strlen(name) : 0;
void *memory = malloc(sizeof(Source) + namel + size + 2);
if(memory == NULL)
{
Error_Report(error, 1, "No memory");
return NULL;
}
Source *s = memory;
s->name = (char*) (s + 1);
s->body = s->name + namel + 1;
s->size = size;
s->refs = 1;
if(name)
strcpy(s->name, name);
strncpy(s->body, body, size);
return s;
}
+12
View File
@@ -0,0 +1,12 @@
#ifndef SOURCE_H
#define SOURCE_H
#include "error.h"
typedef struct xSource Source;
Source *Source_Copy(Source *s);
void Source_Free(Source *s);
const char *Source_GetName(const Source *s);
const char *Source_GetBody(const Source *s);
unsigned int Source_GetSize(const Source *s);
Source *Source_FromFile(const char *file, Error *error);
Source *Source_FromString(const char *name, const char *body, int size, Error *error);
#endif
+73
View File
@@ -0,0 +1,73 @@
#include <stdlib.h>
#include "stack.h"
#include "defs.h"
struct xStack {
unsigned int size, used;
void *body[];
};
void *Stack_New(int size)
{
if(size < 0)
size = 1024;
Stack *s = malloc(sizeof(Stack) + sizeof(void*) * size);
if(s == NULL)
return NULL;
s->size = size;
s->used = 0;
return s;
}
void *Stack_Top(Stack *s, int n)
{
assert(n <= 0);
if(s->used == 0)
return NULL;
if((int) s->used + n - 1 < 0)
return NULL;
return s->body[s->used + n - 1];
}
_Bool Stack_Pop(Stack *s, unsigned int n)
{
if(s->used < n)
return 0;
s->used -= n;
return 1;
}
_Bool Stack_Push(Stack *s, void *item)
{
assert(s != NULL);
assert(item != NULL);
if(s->used == s->size)
return 0;
s->body[s->used] = item;
s->used += 1;
return 1;
}
void Stack_Free(Stack *s)
{
free(s);
}
unsigned int Stack_Size(Stack *s)
{
return s->used;
}
unsigned int Stack_Capacity(Stack *s)
{
return s->size;
}
+11
View File
@@ -0,0 +1,11 @@
#ifndef STACK_H
#define STACK_H
typedef struct xStack Stack;
void *Stack_New(int size);
void *Stack_Top(Stack *s, int n);
_Bool Stack_Pop(Stack *s, unsigned int n);
_Bool Stack_Push(Stack *s, void *item);
void Stack_Free(Stack *s);
unsigned int Stack_Size(Stack *s);
unsigned int Stack_Capacity(Stack *s);
#endif
+2
View File
@@ -0,0 +1,2 @@
./build/test-parse tests/aux/parsing-tests.json
./build/test-objects
+104
View File
@@ -0,0 +1,104 @@
[
{
"source": "1",
"expect": {
"node-kind": "expr",
"expr-kind": "int",
"value": 1
}
},
{
"source": "1.1",
"expect": {
"node-kind": "expr",
"expr-kind": "float",
"value": 1.1
}
},
{
"source": "1+1",
"expect": {
"node-kind": "expr",
"expr-kind": "add",
"operands": [
{"node-kind": "expr", "expr-kind": "int", "value": 1},
{"node-kind": "expr", "expr-kind": "int", "value": 1}
]
}
},
{
"source": "1-1",
"expect": {
"node-kind": "expr",
"expr-kind": "sub",
"operands": [
{"node-kind": "expr", "expr-kind": "int", "value": 1},
{"node-kind": "expr", "expr-kind": "int", "value": 1}
]
}
},
{
"source": "1*1",
"expect": {
"node-kind": "expr",
"expr-kind": "mul",
"operands": [
{"node-kind": "expr", "expr-kind": "int", "value": 1},
{"node-kind": "expr", "expr-kind": "int", "value": 1}
]
}
},
{
"source": "1-+1",
"expect": {
"node-kind": "expr",
"expr-kind": "sub",
"operands": [
{"node-kind": "expr", "expr-kind": "int", "value": 1},
{
"node-kind": "expr",
"expr-kind": "pos",
"operands": [
{"node-kind": "expr", "expr-kind": "int", "value": 1}
]
}
]
}
},
{
"source": "+1",
"expect": {
"node-kind": "expr",
"expr-kind": "pos",
"operands": [
{"node-kind": "expr", "expr-kind": "int", "value": 1}
]
}
},
{
"source": "-1",
"expect": {
"node-kind": "expr",
"expr-kind": "neg",
"operands": [
{"node-kind": "expr", "expr-kind": "int", "value": 1}
]
}
},
{
"source": "hello",
"expect": {"node-kind": "expr", "expr-kind": "ident", "value": "hello"}
},
{
"source": "'hello'",
"expect": {"node-kind": "expr", "expr-kind": "string", "value": "hello"}
},
{
"source": "-'hello'",
"expect": {"node-kind": "expr", "expr-kind": "neg", "operands": [{"node-kind": "expr", "expr-kind": "string", "value": "hello"}]}
},
{
"source": "-'hel\\nlo'",
"expect": {"node-kind": "expr", "expr-kind": "neg", "operands": [{"node-kind": "expr", "expr-kind": "string", "value": "hel\\nlo"}]}
}
]
+86
View File
@@ -0,0 +1,86 @@
#include <assert.h>
#include <stdio.h>
#include "../../src/objects/objects.h"
#define tassert(exp) if(!(exp)) goto done;
#define tdone done: if(1)
static _Bool test_int()
{
_Bool passed = 0;
Heap *heap = Heap_New(-1);
assert(heap != NULL);
Error error;
Error_Init(&error);
// Test [Object_FromInt], [Object_ToInt].
int n = -3; // Just any value.
Object *obj = Object_FromInt(n, heap, &error);
tassert(obj != 0);
tassert(error.occurred == 0);
int k = Object_ToInt(obj, &error);
tassert(k == n);
tassert(error.occurred == 0);
passed = 1;
tdone {
Error_Free(&error);
Heap_Free(heap);
return passed;
}
}
static _Bool test_float()
{
_Bool passed = 0;
Heap *heap = Heap_New(-1);
assert(heap != NULL);
Error error;
Error_Init(&error);
// Test [Object_FromFloat], [Object_ToFloat].
double n = -3.4; // Just any value.
Object *obj = Object_FromFloat(n, heap, &error);
if(obj == 0)
goto done;
double k = Object_ToFloat(obj, &error);
if(k != n)
goto done;
// ...
passed = 1;
done:
Error_Free(&error);
Heap_Free(heap);
return passed;
}
int main()
{
typedef _Bool (*test_t)();
static const test_t tests[] = {
test_int,
test_float,
};
for(int i = 0; i < (int) (sizeof(tests) / sizeof(tests[0])); i += 1)
printf("TEST %d: %s\n", i, test_int() ? "Passed" : "Failed");
return 0;
}
+281
View File
@@ -0,0 +1,281 @@
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <stdio.h>
#include <xjson.h>
#include "../../src/compiler/parse.h"
#include "../../src/compiler/serialize.h"
typedef enum { PASSED, FAILED, IGNORED } result_t;
static _Bool compare(const char *s, xj_value *v)
{
xj_alloc *alloc = NULL;
xj_error error;
xj_value *doc;
doc = xj_decode(&error, &alloc, s, -1);
assert(doc != NULL);
_Bool same = xj_compare(doc, v);
xj_free(alloc);
return same;
}
result_t runtest(xj_value *value, int testno)
{
int raw_sourcel;
char *raw_source;
xj_value *expect;
{
xj_error error;
xj_value *source2 = xj_query(&error, value, ".source", -1);
if(source2 == NULL)
{
if(error.code == xj_E4009)
fprintf(stderr, "WARNING: Field \"source\" is missing\n");
else
fprintf(stderr, "WARNING: Internal failure (%s)\n", error.text);
return IGNORED;
}
if(source2->type != xj_STRING)
{
fprintf(stderr, "WARNING: Field \"source\" is not a string\n");
return IGNORED;
}
raw_source = source2->as_string;
raw_sourcel = source2->size;
expect = xj_query(&error, value, ".expect", -1);
if(expect == NULL)
{
if(error.code == xj_E4009)
fprintf(stderr, "WARNING: Field \"expect\" is missing\n");
else
fprintf(stderr, "WARNING: Internal failure (%s)\n", error.text);
return IGNORED;
}
if(expect->type != xj_OBJECT)
{
fprintf(stderr, "WARNING: Field \"expect\" is not an object\n");
return IGNORED;
}
}
Source *source;
{
Error error;
Error_Init(&error);
char temp[64];
int k = snprintf(temp, sizeof(temp), "(Test %d's source)", testno);
assert(k >= 0);
assert((unsigned int) k < sizeof(temp));
source = Source_FromString(temp, raw_source, raw_sourcel, &error);
if(source == NULL)
{
fprintf(stderr, "WARNING: Internal failure (%s)\n", error.message);
return IGNORED;
}
}
_Bool passed;
{
Error error;
BPAlloc *alloc;
char buffer[65536];
Error_Init(&error);
alloc = BPAlloc_Init3(buffer, sizeof(buffer), -1, NULL, NULL, NULL);
if(alloc == NULL)
{
fprintf(stderr, "WARNING: Internal failure (Failed to instanciate BPAlloc)\n");
return IGNORED;
}
AST *ast = parse(source, alloc, &error);
if(ast == NULL)
{
fprintf(stderr, "WARNING: Internal failure (Failed to compile: %s; reported in %s at %s:%d)\n", error.message, error.func, error.file, error.line);
Error_Free(&error);
BPAlloc_Free(alloc);
return IGNORED;
}
char *serialized = serialize(ast, NULL);
if(serialized == NULL)
{
fprintf(stderr, "WARNING: Internal failure (Failed to serialize AST)\n");
Error_Free(&error);
BPAlloc_Free(alloc);
return IGNORED;
}
/*
printf("SERIALIZED :: %s\n", serialized);
{
char *temp = xj_encode(expect, NULL);
printf("SERIALIZED 2 :: %s\n", temp);
xj_sys_free(temp);
}
*/
passed = compare(serialized, expect);
Error_Free(&error);
BPAlloc_Free(alloc);
xj_sys_free(serialized);
}
return passed ? PASSED : FAILED;
}
static char *loadfile(const char *file, int *size);
int main(int argc, char **argv)
{
if(argc == 1)
{
fprintf(stderr, "ERROR: Missing file name\n");
return 1;
}
int filesize;
char *filename = argv[1];
char *filetext = loadfile(filename, &filesize);
if(filetext == NULL)
{
fprintf(stderr, "ERROR: Couldn't open \"%s\"\n", filename);
return 1;
}
xj_alloc *alloc = NULL;
xj_error error;
xj_value *document;
document = xj_decode(&error, &alloc, filetext, filesize);
free(filetext);
if(document == NULL)
{
assert(error.occurred == 1);
fprintf(stderr, "ERROR: Failed to parse \"%s\" as JSON: %s\n",
filename, error.text);
xj_free(alloc);
return 1;
}
if(document->type != xj_ARRAY)
{
fprintf(stderr, "ERROR: Was expected an array as JSON document root\n");
xj_free(alloc);
return 1;
}
xj_value *cursor = document->as_array;
int passed = 0, ignored = 0;
int total = document->size;
int index = 1;
while(cursor)
{
result_t result = runtest(cursor, index);
#define KNRM "\x1B[0m"
#define KRED "\x1B[31m"
#define KGRN "\x1B[32m"
#define KYEL "\x1B[33m"
#define KBLU "\x1B[34m"
#define KMAG "\x1B[35m"
#define KCYN "\x1B[36m"
#define KWHT "\x1B[37m"
#define RESET "\033[0m"
switch(result)
{
case PASSED:
printf("TEST %-2d :: " KGRN "PASSED" RESET "\n", index);
passed += 1;
break;
case FAILED:
printf("TEST %-2d :: " KRED "FAILED" RESET "\n", index);
break;
case IGNORED:
printf("TEST %-2d :: " KYEL "IGNORED" RESET "\n", index);
ignored += 1;
break;
default: assert(0);
}
index += 1;
cursor = cursor->next;
}
printf("\n ... %d passed, %d failed and %d ignored.\n", passed, total - passed - ignored, ignored);
xj_free(alloc);
return 0;
}
static char *loadfile(const char *file, int *size)
{
char *body = NULL;
int _size;
FILE *fp = fopen(file, "rb");
if(fp == NULL)
return NULL;
if(fseek(fp, 0, SEEK_END))
goto done;
_size = ftell(fp);
if(_size < 0)
goto done;
if(size)
*size = _size;
body = malloc(_size + 1);
if(body == NULL)
goto done;
if(fseek(fp, 0, SEEK_SET))
goto done;
int k = fread(body, 1, _size, fp);
if(k != _size)
goto done;
body[_size] = '\0';
done:
fclose(fp);
return body;
}