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
+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