cleanups and more test cases
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
|
||||
The source files directly contained by this folder expose to the user through the command line the functionalities implemented inside the subfolders. The `main.c` file implements the entry of the program and, based on the options provided by the user, executes, parses or disassembles the noja source code. In `debug.c` is implemented the callback that `main.c` provides to the runtime if the user asks for an execution in debug mode that makes it possible to expose the internal state of the interpreter during the execution.
|
||||
|
||||
* The `utils` folder contains the implementations of general purpose data structures and definitions that are useful through all of the codebase. Some of the more used data structures implemented in it are:
|
||||
|
||||
* `BPAlloc`: A bump-pointer allocator. It's useful during the compilation phase because lots of small objects need to be allocated and then freed at the same time when the final executable is produced.
|
||||
* `Error`: A structure useful to report errors to function callers.
|
||||
* `Source`: A string object that is used in place of raw strings to move source code around.
|
||||
|
||||
* The `common` folder implements the `Executable` data structure, which contains the result of a source's compilation. It can be though about as an array of bytecode instructions that can be directly executed.
|
||||
|
||||
* The `compiler` folder implements the compiler of the interpreter. The main routine that is exported from here is `compile`, which transforms a `Source` into an `Executable`. Other functions are exported like `serialize` that transforms an `AST` to a JSON string. This subfolder is the only part of the codebase that should be able to access the `AST` nodes.
|
||||
|
||||
* The `objects` folder implements the object model. In the context of this language, an object is a virtual class that implements a given set of methods. This folder exports functions that transform "raw" data types into objects, functions that do the inverse transformation and functions that trigger the virtual methods. This folder also contains the implementation of the heap and the garbage collector that needs to be tightly coupled with the object model.
|
||||
|
||||
* The `runtime` folder implements the routines that run the `Executable`. It depends heavily on `objects`. It basically iterates over the executable's bytecode and applies the described actions to the objects.
|
||||
It also implements a couple of objects that can't be implemented inside `objects` because they depend on the runtime internals.
|
||||
@@ -0,0 +1,393 @@
|
||||
#include <ctype.h>
|
||||
#include <limits.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include "../utils/error.h"
|
||||
#include "../utils/source.h"
|
||||
#include "../utils/labellist.h"
|
||||
#include "../common/executable.h"
|
||||
|
||||
typedef struct {
|
||||
const char *str;
|
||||
size_t len;
|
||||
size_t cur;
|
||||
} Context;
|
||||
|
||||
static void skipIdentifier(Context *ctx)
|
||||
{
|
||||
while(ctx->cur < ctx->len && (isalpha(ctx->str[ctx->cur]) || isdigit(ctx->str[ctx->cur]) || ctx->str[ctx->cur] == '_'))
|
||||
ctx->cur += 1;
|
||||
}
|
||||
|
||||
static void skipSpaces(Context *ctx)
|
||||
{
|
||||
while(ctx->cur < ctx->len && isspace(ctx->str[ctx->cur]))
|
||||
ctx->cur += 1;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
size_t offset;
|
||||
size_t length;
|
||||
} Slice;
|
||||
|
||||
static bool parseLabelAndOpcode(Context *ctx, bool *no_label,
|
||||
Slice *label, Slice *opcode,
|
||||
Error *error)
|
||||
{
|
||||
assert(ctx != NULL && no_label != NULL
|
||||
&& label != NULL && opcode != NULL);
|
||||
|
||||
// NOTE: This function must start at
|
||||
// the first byte of the label or
|
||||
// opcode. All whitespace must be
|
||||
// consumed by the caller.
|
||||
|
||||
// Now we expect either a label and an
|
||||
// opcode, or just an opcode
|
||||
//
|
||||
// <label>: <opcode> ..operands..
|
||||
// <opcode> ..operands..
|
||||
//
|
||||
// Labels and opcodes can bot contain
|
||||
// digits, letters and underscores.
|
||||
|
||||
Slice label_or_opcode;
|
||||
{
|
||||
char c = ctx->str[ctx->cur];
|
||||
if(!isalpha(c) && c != '_') {
|
||||
// ERROR: Missing opcode
|
||||
Error_Report(error, 0, "Missing opcode");
|
||||
}
|
||||
|
||||
label_or_opcode.offset = ctx->cur;
|
||||
skipIdentifier(ctx);
|
||||
label_or_opcode.length = ctx->cur - label_or_opcode.offset;
|
||||
|
||||
assert(label_or_opcode.length > 0);
|
||||
}
|
||||
|
||||
// Get the character after the label or opcode
|
||||
// (ignoring whitespace), and if it's a ':',
|
||||
// then it was a label.
|
||||
skipSpaces(ctx);
|
||||
if(ctx->cur < ctx->len && ctx->str[ctx->cur] == ':') {
|
||||
|
||||
// Skip the ':' and the whitespace after it.
|
||||
ctx->cur += 1;
|
||||
skipSpaces(ctx);
|
||||
|
||||
if(ctx->cur == ctx->len || (!isalpha(ctx->str[ctx->cur]) && ctx->str[ctx->cur] != '_')) {
|
||||
Error_Report(error, 0, "Missing opcode after label");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Now the opcode is expected.
|
||||
opcode->offset = ctx->cur;
|
||||
skipIdentifier(ctx);
|
||||
opcode->length = ctx->cur - opcode->offset;
|
||||
assert(opcode->length > 0);
|
||||
|
||||
*no_label = false;
|
||||
*label = label_or_opcode;
|
||||
skipSpaces(ctx);
|
||||
} else {
|
||||
*no_label = true;
|
||||
*opcode = label_or_opcode;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool parseStringOperand(Context *ctx, Error *error, BPAlloc *alloc, Operand *op)
|
||||
{
|
||||
// Skip the first double quote.
|
||||
assert(ctx->cur < ctx->len && ctx->str[ctx->cur] == '"');
|
||||
ctx->cur += 1;
|
||||
|
||||
size_t literal_offset = ctx->cur;
|
||||
|
||||
// For now do a dumb copy into the buffer
|
||||
// without considering special characters.
|
||||
while(ctx->cur < ctx->len && ctx->str[ctx->cur] != '"')
|
||||
ctx->cur += 1;
|
||||
|
||||
if(ctx->cur == ctx->len) {
|
||||
Error_Report(error, 0, "End of source inside a string literal");
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t literal_length = ctx->cur - literal_offset;
|
||||
|
||||
// Skip the ending double quotes.
|
||||
assert(ctx->cur < ctx->len && ctx->str[ctx->cur] == '"');
|
||||
ctx->cur += 1;
|
||||
|
||||
char *copy = BPAlloc_Malloc(alloc, literal_length+1);
|
||||
if(copy == NULL) {
|
||||
Error_Report(error, 1, "No memory");
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(copy, ctx->str + literal_offset, literal_length);
|
||||
copy[literal_length] = '\0';
|
||||
|
||||
op->type = OPTP_STRING;
|
||||
op->as_string = copy;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool parseIntegerOperand(Context *ctx, Error *error, Operand *op)
|
||||
{
|
||||
// It's ensured by the caller that the cursor is
|
||||
// pointing to a sequence of one or more digits
|
||||
// NOT followed by a dot and a digit after that
|
||||
// (so this is an integer for sure, not a float).
|
||||
assert(ctx->cur < ctx->len && isdigit(ctx->str[ctx->cur]));
|
||||
|
||||
long long int buffer = 0;
|
||||
do {
|
||||
// Transform each digit into it's integer value.
|
||||
int d = ctx->str[ctx->cur] - '0';
|
||||
assert(d >= 0 && d <= 9);
|
||||
|
||||
// Will this overflow?
|
||||
if(buffer > (LLONG_MAX - d) / 10) {
|
||||
Error_Report(error, 0, "Integer literal is too big to be represented in %d bits", 8*sizeof(buffer));
|
||||
return false;
|
||||
}
|
||||
|
||||
buffer = buffer * 10 + d;
|
||||
ctx->cur += 1;
|
||||
} while(ctx->cur < ctx->len && isdigit(ctx->str[ctx->cur]));
|
||||
|
||||
// Not a float!
|
||||
|
||||
op->type = OPTP_INT;
|
||||
op->as_int = buffer;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool parseFloatingOperand(Context *ctx, Error *error, Operand *op)
|
||||
{
|
||||
(void) error; // At the moment this function doesn't report
|
||||
// any error. This may change when overflows
|
||||
// and underflows are detected.
|
||||
|
||||
// It's ensured by the caller that the cursor is
|
||||
// pointing to a sequence of one or more digits
|
||||
// followed by a dot and a digit after that.
|
||||
assert(ctx->cur < ctx->len && isdigit(ctx->str[ctx->cur]));
|
||||
|
||||
double buffer = 0;
|
||||
do {
|
||||
// Transform each digit into it's integer value.
|
||||
int d = ctx->str[ctx->cur] - '0';
|
||||
assert(d >= 0 && d <= 9);
|
||||
|
||||
// Should overflow be checked?
|
||||
buffer = buffer * 10 + d;
|
||||
|
||||
ctx->cur += 1;
|
||||
} while(ctx->str[ctx->cur] != '.');
|
||||
|
||||
assert(ctx->cur+1 < ctx->len && ctx->str[ctx->cur] == '.' && isdigit(ctx->str[ctx->cur+1]));
|
||||
|
||||
// Skip the dot.
|
||||
ctx->cur += 1;
|
||||
|
||||
double q = 1;
|
||||
do {
|
||||
// Transform each digit into it's integer value.
|
||||
int d = ctx->str[ctx->cur] - '0';
|
||||
assert(d >= 0 && d <= 9);
|
||||
|
||||
q /= 10;
|
||||
buffer += q * d;
|
||||
|
||||
ctx->cur += 1;
|
||||
} while(ctx->cur < ctx->len && isdigit(ctx->str[ctx->cur]));
|
||||
|
||||
op->type = OPTP_FLOAT;
|
||||
op->as_float = buffer;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool parseOperands(Context *ctx, BPAlloc *alloc, Error *error,
|
||||
LabelList *list, Operand *opv, int *opc, int opc_max)
|
||||
{
|
||||
// NOTE: The whitespace before the first operand must
|
||||
// be consumed by the caller.
|
||||
|
||||
if(ctx->cur < ctx->len && ctx->str[ctx->cur] != ';')
|
||||
while(1) {
|
||||
|
||||
Operand op;
|
||||
|
||||
char c = ctx->str[ctx->cur];
|
||||
if(c == '"') {
|
||||
|
||||
if(!parseStringOperand(ctx, error, alloc, &op))
|
||||
return false;
|
||||
|
||||
} else if(isdigit(c)) {
|
||||
|
||||
/* Integer or float operand */
|
||||
|
||||
size_t k = ctx->cur;
|
||||
while(k < ctx->len && isdigit(ctx->str[k]))
|
||||
k += 1;
|
||||
|
||||
bool ok;
|
||||
if(k+1 >= ctx->len || ctx->str[k] != '.' || !isdigit(ctx->str[k+1]))
|
||||
ok = parseIntegerOperand(ctx, error, &op);
|
||||
else
|
||||
ok = parseFloatingOperand(ctx, error, &op);
|
||||
|
||||
if(!ok)
|
||||
return false;
|
||||
|
||||
} else if(isalpha(c) || c == '_') {
|
||||
|
||||
/* Label */
|
||||
size_t offset = ctx->cur;
|
||||
skipIdentifier(ctx);
|
||||
size_t length = ctx->cur - offset;
|
||||
|
||||
Promise *promise = LabelList_GetLabel(list, ctx->str + offset, length);
|
||||
if(promise == NULL) {
|
||||
Error_Report(error, 1, "No memory");
|
||||
return false;
|
||||
}
|
||||
|
||||
op.type = OPTP_PROMISE;
|
||||
op.as_promise = promise;
|
||||
|
||||
} else {
|
||||
// ERROR: Unexpected character
|
||||
Error_Report(error, 0, "Unexpected character '%c'", c);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(*opc == opc_max) {
|
||||
Error_Report(error, 0, "Too many operands");
|
||||
return false;
|
||||
}
|
||||
opv[*opc] = op;
|
||||
*opc += 1;
|
||||
// Now prepare for the next operand
|
||||
skipSpaces(ctx);
|
||||
|
||||
if(ctx->cur == ctx->len || ctx->str[ctx->cur] == ';')
|
||||
break;
|
||||
|
||||
c = ctx->str[ctx->cur];
|
||||
if(c != ',') {
|
||||
Error_Report(error, 0, "Unexpected character '%c' (',' or ';' were expected)", c);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Skip the comma
|
||||
ctx->cur += 1;
|
||||
|
||||
// Skip the spaces before the next operand
|
||||
skipSpaces(ctx);
|
||||
}
|
||||
assert(ctx->cur == ctx->len || ctx->str[ctx->cur] == ';');
|
||||
return true;
|
||||
}
|
||||
|
||||
Executable *assemble(Source *src, Error *error)
|
||||
{
|
||||
Executable *exe = NULL;
|
||||
|
||||
BPAlloc *alloc = BPAlloc_Init(-1);
|
||||
if(alloc == NULL) {
|
||||
Error_Report(error, 1, "No memory");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
LabelList *list = LabelList_New(alloc);
|
||||
if(list == NULL) {
|
||||
Error_Report(error, 1, "No memory");
|
||||
BPAlloc_Free(alloc);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ExeBuilder *builder = ExeBuilder_New(alloc);
|
||||
if(builder == NULL) {
|
||||
Error_Report(error, 1, "No memory");
|
||||
LabelList_Free(list);
|
||||
BPAlloc_Free(alloc);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Context ctx = {
|
||||
.str = Source_GetBody(src),
|
||||
.len = Source_GetSize(src),
|
||||
.cur = 0,
|
||||
};
|
||||
while(1) {
|
||||
|
||||
skipSpaces(&ctx);
|
||||
|
||||
if(ctx.cur == ctx.len)
|
||||
break;
|
||||
|
||||
bool no_label;
|
||||
Slice label, opcode_name;
|
||||
|
||||
if(!parseLabelAndOpcode(&ctx, &no_label, &label, &opcode_name, error))
|
||||
goto done;
|
||||
|
||||
// If a label was defined, add it to the list.
|
||||
if(no_label == false) {
|
||||
long long int value = ExeBuilder_InstrCount(builder);
|
||||
if(!LabelList_SetLabel(list, ctx.str + label.offset, label.length, value)) {
|
||||
Error_Report(error, 1, "Out of memory");
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
// Check that the opcode is valid (at this
|
||||
// point it's just an unchecked identifier)
|
||||
Opcode opcode;
|
||||
const char *name = ctx.str + opcode_name.offset;
|
||||
if(!Executable_GetOpcodeBinaryFromName(name, opcode_name.length, &opcode)) {
|
||||
Error_Report(error, 0, "Opcode %.*s doesn't exist", (int) opcode_name.length, name);
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Parse operands */
|
||||
// (whitespace was already skipped)
|
||||
|
||||
Operand opv[8];
|
||||
int opc = 0;
|
||||
|
||||
if(!parseOperands(&ctx, alloc, error, list, opv, &opc, sizeof(opv)/sizeof(opv[0])))
|
||||
goto done;
|
||||
|
||||
// The operand list ended with a ';' or the
|
||||
// end of the file. If the file didn't end,
|
||||
// then the ';' must be consumed.
|
||||
assert(ctx.cur == ctx.len || ctx.str[ctx.cur] == ';');
|
||||
if(ctx.cur < ctx.len) ctx.cur += 1;
|
||||
|
||||
if(!ExeBuilder_Append(builder, error, opcode, opv, opc, opcode_name.offset, opcode_name.length))
|
||||
goto done;
|
||||
}
|
||||
|
||||
size_t unresolved_count = LabelList_GetUnresolvedCount(list);
|
||||
if(unresolved_count > 0) {
|
||||
Error_Report(error, 0, "%d unresolved labels", unresolved_count);
|
||||
goto done;
|
||||
}
|
||||
|
||||
exe = ExeBuilder_Finalize(builder, error);
|
||||
|
||||
if(exe != NULL)
|
||||
(void) Executable_SetSource(exe, src);
|
||||
|
||||
done:
|
||||
LabelList_Free(list);
|
||||
BPAlloc_Free(alloc);
|
||||
return exe;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
#ifndef ASSEMBLE_H
|
||||
#define ASSEMBLE_H
|
||||
#include "../utils/error.h"
|
||||
#include "../utils/source.h"
|
||||
#include "../common/executable.h"
|
||||
Executable *assemble(Source *src, Error *error);
|
||||
#endif /* ASSEMBLE_H */
|
||||
@@ -0,0 +1,3 @@
|
||||
Here are implemented the language's builtin variables. Each file defines it's values and exposes them through an array of `StaticMapSlot`s. These values can then be accessed from within the language by wrapping these arrays in static map objects (check src/o_staticmap.c to know more).
|
||||
|
||||
The array of `StaticMapSlot`s defined by `basic.c` is the root of the builtin object tree. All other arrays are referred by it.
|
||||
@@ -0,0 +1,401 @@
|
||||
|
||||
/* +--------------------------------------------------------------------------+
|
||||
** | _ _ _ |
|
||||
** | | \ | | (_) |
|
||||
** | | \| | ___ _ __ _ |
|
||||
** | | . ` |/ _ \| |/ _` | |
|
||||
** | | |\ | (_) | | (_| | |
|
||||
** | |_| \_|\___/| |\__,_| |
|
||||
** | _/ | |
|
||||
** | |__/ |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | Copyright (c) 2022 Francesco Cozzuto <francesco.cozzuto@gmail.com> |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | This file is part of The Noja Interpreter. |
|
||||
** | |
|
||||
** | The Noja Interpreter is free software: you can redistribute it and/or |
|
||||
** | modify it under the terms of the GNU General Public License as published |
|
||||
** | by the Free Software Foundation, either version 3 of the License, or (at |
|
||||
** | your option) any later version. |
|
||||
** | |
|
||||
** | The Noja Interpreter is distributed in the hope that it will be useful, |
|
||||
** | but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||||
** | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General |
|
||||
** | Public License for more details. |
|
||||
** | |
|
||||
** | You should have received a copy of the GNU General Public License along |
|
||||
** | with The Noja Interpreter. If not, see <http://www.gnu.org/licenses/>. |
|
||||
** +--------------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include "basic.h"
|
||||
#include "files.h"
|
||||
#include "math.h"
|
||||
#include "../utils/utf8.h"
|
||||
|
||||
static int bin_print(Runtime *runtime, Object **argv, unsigned int argc, Object **rets, unsigned int maxretc, Error *error)
|
||||
{
|
||||
(void) runtime;
|
||||
(void) rets;
|
||||
(void) maxretc;
|
||||
(void) error;
|
||||
|
||||
for(int i = 0; i < (int) argc; i += 1)
|
||||
Object_Print(argv[i], stdout);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bin_type(Runtime *runtime, Object **argv, unsigned int argc, Object **rets, unsigned int maxretc, Error *error)
|
||||
{
|
||||
assert(argc == 1);
|
||||
(void) runtime;
|
||||
(void) error;
|
||||
|
||||
if(maxretc == 0)
|
||||
return 0;
|
||||
rets[0] = (Object*) argv[0]->type;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int bin_unicode(Runtime *runtime, Object **argv, unsigned int argc, Object **rets, unsigned int maxretc, Error *error)
|
||||
{
|
||||
(void) runtime;
|
||||
(void) error;
|
||||
|
||||
assert(argc == 1);
|
||||
|
||||
uint32_t ret = 0;
|
||||
|
||||
if(!Object_IsString(argv[0]))
|
||||
{
|
||||
Error_Report(error, 0, "Argument #%d is not a string", 1);
|
||||
return -1;
|
||||
}
|
||||
|
||||
const char *string;
|
||||
int n;
|
||||
string = Object_ToString(argv[0],&n,Runtime_GetHeap(runtime),error);
|
||||
if (string == NULL)
|
||||
return -1;
|
||||
|
||||
if(n == 0)
|
||||
{
|
||||
Error_Report(error, 0, "Argument #%d is an empty string", 1);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
int k = utf8_sequence_to_utf32_codepoint(string,n,&ret);
|
||||
assert(k >= 0);
|
||||
|
||||
Object *temp = Object_FromInt(ret,Runtime_GetHeap(runtime),error);
|
||||
|
||||
if(temp == NULL)
|
||||
return -1;
|
||||
|
||||
if(maxretc == 0)
|
||||
return 0;
|
||||
rets[0] = temp;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int bin_chr(Runtime *runtime, Object **argv, unsigned int argc, Object **rets, unsigned int maxretc, Error *error)
|
||||
{
|
||||
|
||||
assert(argc == 1);
|
||||
|
||||
|
||||
if(!Object_IsInt(argv[0]))
|
||||
{
|
||||
Error_Report(error, 0, "Argument #%d is not an integer", 1);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
char buff[32];
|
||||
|
||||
int value = Object_ToInt(argv[0],error);
|
||||
|
||||
if(error->occurred)
|
||||
return -1;
|
||||
|
||||
|
||||
int k = utf8_sequence_from_utf32_codepoint(buff,sizeof(buff),value);
|
||||
|
||||
if(k<0)
|
||||
{
|
||||
Error_Report(error, 0, "Argument #%d is not valid utf-32", 1);
|
||||
return -1;
|
||||
}
|
||||
|
||||
Object *temp = Object_FromString(buff,k,Runtime_GetHeap(runtime),error);
|
||||
|
||||
if(temp == NULL)
|
||||
return -1;
|
||||
|
||||
if(maxretc == 0)
|
||||
return 0;
|
||||
rets[0] = temp;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int bin_count(Runtime *runtime, Object **argv, unsigned int argc, Object **rets, unsigned int maxretc, Error *error)
|
||||
{
|
||||
assert(argc == 1);
|
||||
|
||||
int n = Object_Count(argv[0], error);
|
||||
|
||||
if(error->occurred)
|
||||
return -1;
|
||||
|
||||
Object *temp = Object_FromInt(n, Runtime_GetHeap(runtime), error);
|
||||
|
||||
if(temp == NULL)
|
||||
return -1;
|
||||
|
||||
if(maxretc == 0)
|
||||
return 0;
|
||||
rets[0] = temp;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int bin_input(Runtime *runtime, Object **argv, unsigned int argc, Object **rets, unsigned int maxretc, Error *error)
|
||||
{
|
||||
(void) argv;
|
||||
|
||||
assert(argc == 0);
|
||||
|
||||
char maybe[256];
|
||||
char *str = maybe;
|
||||
int size = 0, cap = sizeof(maybe)-1;
|
||||
|
||||
while(1)
|
||||
{
|
||||
char c = getc(stdin);
|
||||
|
||||
if(c == '\n')
|
||||
break;
|
||||
|
||||
if(size == cap)
|
||||
{
|
||||
int newcap = cap*2;
|
||||
char *tmp = realloc(str, newcap + 1);
|
||||
|
||||
if(tmp == NULL)
|
||||
{
|
||||
if(str != maybe) free(str);
|
||||
Error_Report(error, 1, "No memory");
|
||||
return -1;
|
||||
}
|
||||
|
||||
str = tmp;
|
||||
cap = newcap;
|
||||
}
|
||||
|
||||
str[size++] = c;
|
||||
}
|
||||
|
||||
str[size] = '\0';
|
||||
|
||||
Object *res = Object_FromString(str, size, Runtime_GetHeap(runtime), error);
|
||||
|
||||
if(str != maybe)
|
||||
free(str);
|
||||
|
||||
if(res == NULL)
|
||||
return -1;
|
||||
|
||||
if(maxretc == 0)
|
||||
return 0;
|
||||
rets[0] = res;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int bin_assert(Runtime *runtime, Object **argv, unsigned int argc, Object **rets, unsigned int maxretc, Error *error)
|
||||
{
|
||||
(void) runtime;
|
||||
(void) rets;
|
||||
(void) maxretc;
|
||||
|
||||
for(unsigned int i = 0; i < argc; i += 1)
|
||||
if(!Object_ToBool(argv[i], error))
|
||||
{
|
||||
if(!error->occurred)
|
||||
Error_Report(error, 0, "Assertion failed");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bin_error(Runtime *runtime, Object **argv, unsigned int argc, Object **rets, unsigned int maxretc, Error *error)
|
||||
{
|
||||
(void) rets;
|
||||
(void) maxretc;
|
||||
|
||||
assert(argc == 1);
|
||||
|
||||
int length;
|
||||
const char *string;
|
||||
|
||||
string = Object_ToString(argv[0], &length, Runtime_GetHeap(runtime), error);
|
||||
|
||||
if(string == NULL)
|
||||
return -1;
|
||||
|
||||
Error_Report(error, 0, "%s", string);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int bin_strcat(Runtime *runtime, Object **argv, unsigned int argc, Object **rets, unsigned int maxretc, Error *error)
|
||||
{
|
||||
unsigned int total_count = 0;
|
||||
|
||||
for(unsigned int i = 0; i < argc; i += 1)
|
||||
{
|
||||
if(!Object_IsString(argv[i]))
|
||||
{
|
||||
Error_Report(error, 0, "Argument #%d is not a string", i+1);
|
||||
return -1;
|
||||
}
|
||||
|
||||
total_count += Object_Count(argv[i], error);
|
||||
|
||||
if(error->occurred)
|
||||
return -1;
|
||||
}
|
||||
|
||||
char starting[128];
|
||||
char *buffer = starting;
|
||||
|
||||
if(total_count > sizeof(starting)-1)
|
||||
{
|
||||
buffer = malloc(total_count+1);
|
||||
|
||||
if(buffer == NULL)
|
||||
{
|
||||
Error_Report(error, 1, "No memory");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
Object *result = NULL;
|
||||
|
||||
for(unsigned int i = 0, written = 0; i < argc; i += 1)
|
||||
{
|
||||
int n;
|
||||
const char *s = Object_ToString(argv[i], &n,
|
||||
Runtime_GetHeap(runtime), error);
|
||||
|
||||
if(error->occurred)
|
||||
goto done;
|
||||
|
||||
memcpy(buffer + written, s, n);
|
||||
written += n;
|
||||
}
|
||||
|
||||
buffer[total_count] = '\0';
|
||||
|
||||
result = Object_FromString(buffer, total_count, Runtime_GetHeap(runtime), error);
|
||||
|
||||
done:
|
||||
if(starting != buffer)
|
||||
free(buffer);
|
||||
|
||||
if(result == NULL)
|
||||
return -1;
|
||||
|
||||
if(maxretc == 0)
|
||||
return 0;
|
||||
rets[0] = result;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int bin_newBuffer(Runtime *runtime, Object **argv, unsigned int argc, Object **rets, unsigned int maxretc, Error *error)
|
||||
{
|
||||
assert(argc == 1);
|
||||
|
||||
long long int size = Object_ToInt(argv[0], error);
|
||||
|
||||
if(error->occurred == 1)
|
||||
return -1;
|
||||
|
||||
Object *temp = Object_NewBuffer(size, Runtime_GetHeap(runtime), error);
|
||||
|
||||
if(temp == NULL)
|
||||
return -1;
|
||||
|
||||
if(maxretc == 0)
|
||||
return 0;
|
||||
rets[0] = temp;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int bin_sliceBuffer(Runtime *runtime, Object **argv, unsigned int argc, Object **rets, unsigned int maxretc, Error *error)
|
||||
{
|
||||
assert(argc == 3);
|
||||
|
||||
long long int offset = Object_ToInt(argv[1], error);
|
||||
if(error->occurred == 1) return -1;
|
||||
|
||||
long long int length = Object_ToInt(argv[2], error);
|
||||
if(error->occurred == 1) return -1;
|
||||
|
||||
Object *temp = Object_SliceBuffer(argv[0], offset, length, Runtime_GetHeap(runtime), error);
|
||||
|
||||
if(temp == NULL)
|
||||
return -1;
|
||||
|
||||
if(maxretc == 0)
|
||||
return 0;
|
||||
rets[0] = temp;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int bin_bufferToString(Runtime *runtime, Object **argv, unsigned int argc, Object **rets, unsigned int maxretc, Error *error)
|
||||
{
|
||||
assert(argc == 1);
|
||||
|
||||
void *buffaddr;
|
||||
int buffsize;
|
||||
|
||||
buffaddr = Object_GetBufferAddrAndSize(argv[0], &buffsize, error);
|
||||
|
||||
if(error->occurred)
|
||||
return -1;
|
||||
|
||||
Object *temp = Object_FromString(buffaddr, buffsize, Runtime_GetHeap(runtime), error);
|
||||
|
||||
if(temp == NULL)
|
||||
return -1;
|
||||
|
||||
if(maxretc == 0)
|
||||
return 0;
|
||||
rets[0] = temp;
|
||||
return 1;
|
||||
}
|
||||
|
||||
const StaticMapSlot bins_basic[] = {
|
||||
{ "math", SM_SMAP, .as_smap = bins_math, },
|
||||
// { "files", SM_SMAP, .as_smap = bins_files, },
|
||||
// { "net", SM_SMAP, .as_smap = bins_net, },
|
||||
|
||||
{ "newBuffer", SM_FUNCT, .as_funct = bin_newBuffer, .argc = 1 },
|
||||
{ "sliceBuffer", SM_FUNCT, .as_funct = bin_sliceBuffer, .argc = 3 },
|
||||
{ "bufferToString", SM_FUNCT, .as_funct = bin_bufferToString, .argc = 1 },
|
||||
|
||||
{ "strcat", SM_FUNCT, .as_funct = bin_strcat, .argc = -1 },
|
||||
|
||||
{ "type", SM_FUNCT, .as_funct = bin_type, .argc = 1 },
|
||||
{ "unicode", SM_FUNCT, .as_funct = bin_unicode, .argc = 1 },
|
||||
{ "chr", SM_FUNCT, .as_funct = bin_chr, .argc = 1 },
|
||||
{ "print", SM_FUNCT, .as_funct = bin_print, .argc = -1 },
|
||||
{ "input", SM_FUNCT, .as_funct = bin_input, .argc = 0 },
|
||||
{ "count", SM_FUNCT, .as_funct = bin_count, .argc = 1 },
|
||||
{ "error", SM_FUNCT, .as_funct = bin_error, .argc = 1 },
|
||||
{ "assert", SM_FUNCT, .as_funct = bin_assert, .argc = -1 },
|
||||
{ NULL, SM_END, {}, {} },
|
||||
};
|
||||
@@ -0,0 +1,32 @@
|
||||
|
||||
/* +--------------------------------------------------------------------------+
|
||||
** | _ _ _ |
|
||||
** | | \ | | (_) |
|
||||
** | | \| | ___ _ __ _ |
|
||||
** | | . ` |/ _ \| |/ _` | |
|
||||
** | | |\ | (_) | | (_| | |
|
||||
** | |_| \_|\___/| |\__,_| |
|
||||
** | _/ | |
|
||||
** | |__/ |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | Copyright (c) 2022 Francesco Cozzuto <francesco.cozzuto@gmail.com> |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | This file is part of The Noja Interpreter. |
|
||||
** | |
|
||||
** | The Noja Interpreter is free software: you can redistribute it and/or |
|
||||
** | modify it under the terms of the GNU General Public License as published |
|
||||
** | by the Free Software Foundation, either version 3 of the License, or (at |
|
||||
** | your option) any later version. |
|
||||
** | |
|
||||
** | The Noja Interpreter is distributed in the hope that it will be useful, |
|
||||
** | but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||||
** | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General |
|
||||
** | Public License for more details. |
|
||||
** | |
|
||||
** | You should have received a copy of the GNU General Public License along |
|
||||
** | with The Noja Interpreter. If not, see <http://www.gnu.org/licenses/>. |
|
||||
** +--------------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#include "../runtime/runtime.h"
|
||||
extern const StaticMapSlot bins_basic[];
|
||||
@@ -0,0 +1,310 @@
|
||||
|
||||
/* +--------------------------------------------------------------------------+
|
||||
** | _ _ _ |
|
||||
** | | \ | | (_) |
|
||||
** | | \| | ___ _ __ _ |
|
||||
** | | . ` |/ _ \| |/ _` | |
|
||||
** | | |\ | (_) | | (_| | |
|
||||
** | |_| \_|\___/| |\__,_| |
|
||||
** | _/ | |
|
||||
** | |__/ |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | Copyright (c) 2022 Francesco Cozzuto <francesco.cozzuto@gmail.com> |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | This file is part of The Noja Interpreter. |
|
||||
** | |
|
||||
** | The Noja Interpreter is free software: you can redistribute it and/or |
|
||||
** | modify it under the terms of the GNU General Public License as published |
|
||||
** | by the Free Software Foundation, either version 3 of the License, or (at |
|
||||
** | your option) any later version. |
|
||||
** | |
|
||||
** | The Noja Interpreter is distributed in the hope that it will be useful, |
|
||||
** | but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||||
** | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General |
|
||||
** | Public License for more details. |
|
||||
** | |
|
||||
** | You should have received a copy of the GNU General Public License along |
|
||||
** | with The Noja Interpreter. If not, see <http://www.gnu.org/licenses/>. |
|
||||
** +--------------------------------------------------------------------------+
|
||||
*/
|
||||
#warning "Commented out whole file"
|
||||
/*
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include "files.h"
|
||||
|
||||
enum {
|
||||
MD_READ = 0,
|
||||
MD_WRITE = 1,
|
||||
MD_APPEND = 2,
|
||||
};
|
||||
|
||||
static Object *bin_openFile(Runtime *runtime, Object **argv, unsigned int argc, Error *error)
|
||||
{
|
||||
assert(argc == 2);
|
||||
|
||||
if(!Object_IsString(argv[0]))
|
||||
{
|
||||
Error_Report(error, 0, "Expected first argument to be a string, but it's a %s", Object_GetName(argv[0]));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if(!Object_IsInt(argv[1]))
|
||||
{
|
||||
Error_Report(error, 0, "Expected second argument to be an int, but it's a %s", Object_GetName(argv[1]));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Heap *heap = Runtime_GetHeap(runtime);
|
||||
|
||||
const char *path = Object_ToString(argv[0], NULL, heap, error);
|
||||
|
||||
if(error->occurred)
|
||||
return NULL;
|
||||
|
||||
int mode = Object_ToInt(argv[1], error);
|
||||
|
||||
if(error->occurred)
|
||||
return NULL;
|
||||
|
||||
FILE *fp;
|
||||
{
|
||||
const char *mode2;
|
||||
|
||||
switch(mode)
|
||||
{
|
||||
case MD_READ:
|
||||
mode2 = "r";
|
||||
break;
|
||||
|
||||
case MD_READ | MD_WRITE:
|
||||
mode2 = "w+";
|
||||
break;
|
||||
|
||||
case MD_READ | MD_APPEND:
|
||||
case MD_READ | MD_WRITE | MD_APPEND:
|
||||
mode2 = "a";
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(0);
|
||||
break;
|
||||
}
|
||||
|
||||
fp = fopen(path, mode2);
|
||||
|
||||
if(fp == NULL)
|
||||
return Object_NewNone(heap, error);
|
||||
}
|
||||
|
||||
return Object_FromStream(fp, heap, error);
|
||||
}
|
||||
|
||||
static Object *bin_read(Runtime *runtime, Object **argv, unsigned int argc, Error *error)
|
||||
{
|
||||
assert(argc == 3);
|
||||
|
||||
// Arg 0: file
|
||||
// Arg 1: buffer
|
||||
// Arg 2: count
|
||||
|
||||
if(!Object_IsFile(argv[0]))
|
||||
{
|
||||
Error_Report(error, 0, "Expected first argument to be a file, but it's a %s", Object_GetName(argv[0]));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if(!Object_IsBuffer(argv[1]))
|
||||
{
|
||||
Error_Report(error, 0, "Expected second argument to be a buffer, but it's a %s", Object_GetName(argv[1]));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Heap *heap = Runtime_GetHeap(runtime);
|
||||
|
||||
void *buff_addr;
|
||||
int buff_size;
|
||||
|
||||
buff_addr = Object_GetBufferAddrAndSize(argv[1], &buff_size, error);
|
||||
|
||||
int read_size;
|
||||
|
||||
if(Object_IsNone(argv[2]))
|
||||
{
|
||||
read_size = buff_size;
|
||||
}
|
||||
else if(Object_IsInt(argv[2]))
|
||||
{
|
||||
long long int temp = Object_ToInt(argv[2], error);
|
||||
|
||||
if(error->occurred)
|
||||
return NULL;
|
||||
|
||||
read_size = temp; // TODO: Handle potential overflow.
|
||||
|
||||
if(read_size > buff_size)
|
||||
read_size = buff_size;
|
||||
}
|
||||
else
|
||||
{
|
||||
Error_Report(error, 0, "Expected third argument to be an int or none, but it's a %s", Object_GetName(argv[0]));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
FILE *fp = Object_ToStream(argv[0], error);
|
||||
|
||||
if(fp == NULL)
|
||||
return NULL;
|
||||
|
||||
size_t n = fread(buff_addr, 1, read_size, fp);
|
||||
|
||||
return Object_FromInt(n, heap, error);
|
||||
}
|
||||
|
||||
static Object *bin_write(Runtime *runtime, Object **argv, unsigned int argc, Error *error)
|
||||
{
|
||||
assert(argc == 3);
|
||||
|
||||
// Arg 0: file
|
||||
// Arg 1: buffer
|
||||
// Arg 2: count
|
||||
|
||||
if(!Object_IsFile(argv[0]))
|
||||
{
|
||||
Error_Report(error, 0, "Expected first argument to be a file, but it's a %s", Object_GetName(argv[0]));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if(!Object_IsBuffer(argv[1]))
|
||||
{
|
||||
Error_Report(error, 0, "Expected second argument to be a buffer, but it's a %s", Object_GetName(argv[1]));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Heap *heap = Runtime_GetHeap(runtime);
|
||||
|
||||
void *buff_addr;
|
||||
int buff_size;
|
||||
|
||||
buff_addr = Object_GetBufferAddrAndSize(argv[1], &buff_size, error);
|
||||
|
||||
int write_size;
|
||||
|
||||
if(Object_IsNone(argv[2]))
|
||||
{
|
||||
write_size = buff_size;
|
||||
}
|
||||
else if(Object_IsInt(argv[2]))
|
||||
{
|
||||
long long int temp = Object_ToInt(argv[2], error);
|
||||
|
||||
if(error->occurred)
|
||||
return NULL;
|
||||
|
||||
write_size = temp; // TODO: Handle potential overflow.
|
||||
|
||||
if(write_size > buff_size)
|
||||
write_size = buff_size;
|
||||
}
|
||||
else
|
||||
{
|
||||
Error_Report(error, 0, "Expected third argument to be an int or none, but it's a %s", Object_GetName(argv[0]));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
FILE *fp = Object_ToStream(argv[0], error);
|
||||
|
||||
if(fp == NULL)
|
||||
return NULL;
|
||||
|
||||
size_t n = fwrite(buff_addr, 1, write_size, fp);
|
||||
|
||||
return Object_FromInt(n, heap, error);
|
||||
}
|
||||
|
||||
static Object *bin_openDir(Runtime *runtime, Object **argv, unsigned int argc, Error *error)
|
||||
{
|
||||
assert(argc == 1);
|
||||
|
||||
// Arg 0: path
|
||||
|
||||
if(!Object_IsString(argv[0]))
|
||||
{
|
||||
Error_Report(error, 0, "Expected first argument to be a string, but it's a %s", Object_GetName(argv[0]));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Heap *heap = Runtime_GetHeap(runtime);
|
||||
|
||||
const char *path = Object_ToString(argv[0], NULL, heap, error);
|
||||
|
||||
if(error->occurred)
|
||||
return NULL;
|
||||
|
||||
DIR *dir = opendir(path);
|
||||
|
||||
if(dir == NULL)
|
||||
return Object_NewNone(heap, error);
|
||||
|
||||
Object *dob = Object_FromDIR(dir, heap, error);
|
||||
|
||||
if(error->occurred)
|
||||
{
|
||||
(void) closedir(dir);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return dob;
|
||||
}
|
||||
|
||||
static Object *bin_nextDirItem(Runtime *runtime, Object **argv, unsigned int argc, Error *error)
|
||||
{
|
||||
assert(argc == 1);
|
||||
|
||||
// Arg 0: path
|
||||
|
||||
if(!Object_IsDir(argv[0]))
|
||||
{
|
||||
Error_Report(error, 0, "Expected first argument to be a directory, but it's a %s", Object_GetName(argv[0]));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
DIR *dir = Object_ToDIR(argv[0], error);
|
||||
|
||||
if(error->occurred)
|
||||
return NULL;
|
||||
|
||||
assert(dir != NULL);
|
||||
|
||||
Heap *heap = Runtime_GetHeap(runtime);
|
||||
|
||||
errno = 0;
|
||||
|
||||
struct dirent *ent = readdir(dir);
|
||||
|
||||
if(ent == NULL)
|
||||
{
|
||||
if(errno == 0)
|
||||
// Nothing left to read.
|
||||
return Object_NewNone(heap, error);
|
||||
|
||||
// An error occurred.
|
||||
Error_Report(error, 1, "Failed to read directory item");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return Object_FromString(ent->d_name, -1, heap, error);
|
||||
}
|
||||
|
||||
const StaticMapSlot bins_files[] = {
|
||||
{ "READ", SM_INT, .as_int = MD_READ, },
|
||||
{ "WRITE", SM_INT, .as_int = MD_WRITE, },
|
||||
{ "APPEND", SM_INT, .as_int = MD_APPEND, },
|
||||
{ "openFile", SM_FUNCT, .as_funct = bin_openFile, .argc = 2, },
|
||||
{ "openDir", SM_FUNCT, .as_funct = bin_openDir, .argc = 1, },
|
||||
{ "nextDirItem", SM_FUNCT, .as_funct = bin_nextDirItem, .argc = 1, },
|
||||
{ "read", SM_FUNCT, .as_funct = bin_read, .argc = 3, },
|
||||
{ "write", SM_FUNCT, .as_funct = bin_write, .argc = 3, },
|
||||
{ NULL, SM_END, {}, {} },
|
||||
};
|
||||
*/
|
||||
@@ -0,0 +1,32 @@
|
||||
|
||||
/* +--------------------------------------------------------------------------+
|
||||
** | _ _ _ |
|
||||
** | | \ | | (_) |
|
||||
** | | \| | ___ _ __ _ |
|
||||
** | | . ` |/ _ \| |/ _` | |
|
||||
** | | |\ | (_) | | (_| | |
|
||||
** | |_| \_|\___/| |\__,_| |
|
||||
** | _/ | |
|
||||
** | |__/ |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | Copyright (c) 2022 Francesco Cozzuto <francesco.cozzuto@gmail.com> |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | This file is part of The Noja Interpreter. |
|
||||
** | |
|
||||
** | The Noja Interpreter is free software: you can redistribute it and/or |
|
||||
** | modify it under the terms of the GNU General Public License as published |
|
||||
** | by the Free Software Foundation, either version 3 of the License, or (at |
|
||||
** | your option) any later version. |
|
||||
** | |
|
||||
** | The Noja Interpreter is distributed in the hope that it will be useful, |
|
||||
** | but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||||
** | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General |
|
||||
** | Public License for more details. |
|
||||
** | |
|
||||
** | You should have received a copy of the GNU General Public License along |
|
||||
** | with The Noja Interpreter. If not, see <http://www.gnu.org/licenses/>. |
|
||||
** +--------------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#include "../runtime/runtime.h"
|
||||
extern const StaticMapSlot bins_files[];
|
||||
@@ -0,0 +1,135 @@
|
||||
|
||||
/* +--------------------------------------------------------------------------+
|
||||
** | _ _ _ |
|
||||
** | | \ | | (_) |
|
||||
** | | \| | ___ _ __ _ |
|
||||
** | | . ` |/ _ \| |/ _` | |
|
||||
** | | |\ | (_) | | (_| | |
|
||||
** | |_| \_|\___/| |\__,_| |
|
||||
** | _/ | |
|
||||
** | |__/ |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | Copyright (c) 2022 Francesco Cozzuto <francesco.cozzuto@gmail.com> |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | This file is part of The Noja Interpreter. |
|
||||
** | |
|
||||
** | The Noja Interpreter is free software: you can redistribute it and/or |
|
||||
** | modify it under the terms of the GNU General Public License as published |
|
||||
** | by the Free Software Foundation, either version 3 of the License, or (at |
|
||||
** | your option) any later version. |
|
||||
** | |
|
||||
** | The Noja Interpreter is distributed in the hope that it will be useful, |
|
||||
** | but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||||
** | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General |
|
||||
** | Public License for more details. |
|
||||
** | |
|
||||
** | You should have received a copy of the GNU General Public License along |
|
||||
** | with The Noja Interpreter. If not, see <http://www.gnu.org/licenses/>. |
|
||||
** +--------------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include "math.h"
|
||||
|
||||
#define WRAP_FUNC(name) \
|
||||
static int bin_ ## name(Runtime *runtime, Object **argv, unsigned int argc, Object **rets, unsigned int maxretc, Error *error) \
|
||||
{ \
|
||||
assert(argc == 1); \
|
||||
\
|
||||
if(Object_IsFloat(argv[0])) \
|
||||
{ \
|
||||
double v = Object_ToFloat(argv[0], error); \
|
||||
\
|
||||
if(error->occurred) \
|
||||
return -1; \
|
||||
\
|
||||
if(maxretc > 0) \
|
||||
{ \
|
||||
rets[0] = Object_FromFloat(name(v), Runtime_GetHeap(runtime), error); \
|
||||
if(rets[0] == NULL) \
|
||||
return -1; \
|
||||
return 1; \
|
||||
} \
|
||||
return 0; \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
Error_Report(error, 0, "Expected first argument to be a float, but it's a %s", Object_GetName(argv[0]));\
|
||||
return -1; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define WRAP_FUNC_2(name) \
|
||||
static int bin_ ## name(Runtime *runtime, Object **argv, unsigned int argc, Object **rets, unsigned int maxretc, Error *error) \
|
||||
{ \
|
||||
assert(argc == 2); \
|
||||
\
|
||||
if(!Object_IsFloat(argv[0])) \
|
||||
{ \
|
||||
Error_Report(error, 0, "Expected first argument to be a float, but it's a %s", Object_GetName(argv[0]));\
|
||||
return -1; \
|
||||
} \
|
||||
\
|
||||
if(!Object_IsFloat(argv[1])) \
|
||||
{ \
|
||||
Error_Report(error, 0, "Expected second argument to be a float, but it's a %s", Object_GetName(argv[1]));\
|
||||
return -1; \
|
||||
} \
|
||||
\
|
||||
double v1 = Object_ToFloat(argv[0], error); \
|
||||
\
|
||||
if(error->occurred) \
|
||||
return -1; \
|
||||
\
|
||||
double v2 = Object_ToFloat(argv[1], error); \
|
||||
\
|
||||
if(error->occurred) \
|
||||
return -1; \
|
||||
\
|
||||
Object *res = Object_FromFloat(name(v1, v2), Runtime_GetHeap(runtime), error); \
|
||||
if(res == NULL) return -1; \
|
||||
if(maxretc == 0) return 0; \
|
||||
rets[0] = res; \
|
||||
return 1; \
|
||||
}
|
||||
|
||||
WRAP_FUNC(ceil)
|
||||
WRAP_FUNC(floor)
|
||||
WRAP_FUNC(sin)
|
||||
WRAP_FUNC(cos)
|
||||
WRAP_FUNC(tan)
|
||||
WRAP_FUNC(asin)
|
||||
WRAP_FUNC(acos)
|
||||
WRAP_FUNC(atan)
|
||||
WRAP_FUNC(exp)
|
||||
WRAP_FUNC(log)
|
||||
WRAP_FUNC(log10)
|
||||
WRAP_FUNC(sqrt)
|
||||
WRAP_FUNC_2(atan2)
|
||||
WRAP_FUNC_2(pow)
|
||||
|
||||
const StaticMapSlot bins_math[] = {
|
||||
{ "PI", SM_FLOAT, .as_float = M_PI },
|
||||
{ "E", SM_FLOAT, .as_float = M_E },
|
||||
|
||||
{ "floor", SM_FUNCT, .as_funct = bin_floor, .argc = 1, },
|
||||
{ "ceil", SM_FUNCT, .as_funct = bin_ceil, .argc = 1, },
|
||||
|
||||
{ "cos", SM_FUNCT, .as_funct = bin_cos, .argc = 1, },
|
||||
{ "sin", SM_FUNCT, .as_funct = bin_sin, .argc = 1, },
|
||||
{ "tan", SM_FUNCT, .as_funct = bin_tan, .argc = 1, },
|
||||
|
||||
{ "acos", SM_FUNCT, .as_funct = bin_acos, .argc = 1, },
|
||||
{ "asin", SM_FUNCT, .as_funct = bin_asin, .argc = 1, },
|
||||
{ "atan", SM_FUNCT, .as_funct = bin_atan, .argc = 1, },
|
||||
{ "atan2", SM_FUNCT, .as_funct = bin_atan2, .argc = 2, },
|
||||
|
||||
{ "exp", SM_FUNCT, .as_funct = bin_exp, .argc = 1, },
|
||||
{ "log", SM_FUNCT, .as_funct = bin_log, .argc = 1, },
|
||||
{ "log10", SM_FUNCT, .as_funct = bin_log10, .argc = 1, },
|
||||
|
||||
{ "pow", SM_FUNCT, .as_funct = bin_pow, .argc = 1, },
|
||||
{ "sqrt", SM_FUNCT, .as_funct = bin_sqrt, .argc = 1, },
|
||||
{ NULL, SM_END, {}, {} },
|
||||
};
|
||||
@@ -0,0 +1,32 @@
|
||||
|
||||
/* +--------------------------------------------------------------------------+
|
||||
** | _ _ _ |
|
||||
** | | \ | | (_) |
|
||||
** | | \| | ___ _ __ _ |
|
||||
** | | . ` |/ _ \| |/ _` | |
|
||||
** | | |\ | (_) | | (_| | |
|
||||
** | |_| \_|\___/| |\__,_| |
|
||||
** | _/ | |
|
||||
** | |__/ |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | Copyright (c) 2022 Francesco Cozzuto <francesco.cozzuto@gmail.com> |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | This file is part of The Noja Interpreter. |
|
||||
** | |
|
||||
** | The Noja Interpreter is free software: you can redistribute it and/or |
|
||||
** | modify it under the terms of the GNU General Public License as published |
|
||||
** | by the Free Software Foundation, either version 3 of the License, or (at |
|
||||
** | your option) any later version. |
|
||||
** | |
|
||||
** | The Noja Interpreter is distributed in the hope that it will be useful, |
|
||||
** | but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||||
** | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General |
|
||||
** | Public License for more details. |
|
||||
** | |
|
||||
** | You should have received a copy of the GNU General Public License along |
|
||||
** | with The Noja Interpreter. If not, see <http://www.gnu.org/licenses/>. |
|
||||
** +--------------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#include "../runtime/runtime.h"
|
||||
extern const StaticMapSlot bins_math[];
|
||||
@@ -0,0 +1,625 @@
|
||||
|
||||
/* +--------------------------------------------------------------------------+
|
||||
** | _ _ _ |
|
||||
** | | \ | | (_) |
|
||||
** | | \| | ___ _ __ _ |
|
||||
** | | . ` |/ _ \| |/ _` | |
|
||||
** | | |\ | (_) | | (_| | |
|
||||
** | |_| \_|\___/| |\__,_| |
|
||||
** | _/ | |
|
||||
** | |__/ |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | Copyright (c) 2022 Francesco Cozzuto <francesco.cozzuto@gmail.com> |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | This file is part of The Noja Interpreter. |
|
||||
** | |
|
||||
** | The Noja Interpreter is free software: you can redistribute it and/or |
|
||||
** | modify it under the terms of the GNU General Public License as published |
|
||||
** | by the Free Software Foundation, either version 3 of the License, or (at |
|
||||
** | your option) any later version. |
|
||||
** | |
|
||||
** | The Noja Interpreter is distributed in the hope that it will be useful, |
|
||||
** | but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||||
** | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General |
|
||||
** | Public License for more details. |
|
||||
** | |
|
||||
** | You should have received a copy of the GNU General Public License along |
|
||||
** | with The Noja Interpreter. If not, see <http://www.gnu.org/licenses/>. |
|
||||
** +--------------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.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 opcount;
|
||||
OperandType *optypes;
|
||||
} InstrInfo;
|
||||
|
||||
static const InstrInfo instr_table[] = {
|
||||
|
||||
[OPCODE_NOPE] = {"NOPE", 0, NULL},
|
||||
|
||||
[OPCODE_POS] = {"POS", 0, NULL},
|
||||
[OPCODE_NEG] = {"NEG", 0, NULL},
|
||||
[OPCODE_NOT] = {"NOT", 0, NULL},
|
||||
|
||||
[OPCODE_ADD] = {"ADD", 0, NULL},
|
||||
[OPCODE_SUB] = {"SUB", 0, NULL},
|
||||
[OPCODE_MUL] = {"MUL", 0, NULL},
|
||||
[OPCODE_DIV] = {"DIV", 0, NULL},
|
||||
|
||||
[OPCODE_EQL] = {"EQL", 0, NULL},
|
||||
[OPCODE_NQL] = {"NQL", 0, NULL},
|
||||
[OPCODE_LSS] = {"LSS", 0, NULL},
|
||||
[OPCODE_GRT] = {"GRT", 0, NULL},
|
||||
[OPCODE_LEQ] = {"LEQ", 0, NULL},
|
||||
[OPCODE_GEQ] = {"GEQ", 0, NULL},
|
||||
[OPCODE_AND] = {"AND", 0, NULL},
|
||||
[OPCODE_OR] = {"OR", 0, NULL},
|
||||
|
||||
[OPCODE_ASS] = {"ASS", 1, (OperandType[]) {OPTP_STRING}},
|
||||
[OPCODE_POP] = {"POP", 1, (OperandType[]) {OPTP_INT}},
|
||||
[OPCODE_CALL] = {"CALL", 2, (OperandType[]) {OPTP_INT, OPTP_INT}},
|
||||
[OPCODE_SELECT] = {"SELECT", 0, NULL},
|
||||
[OPCODE_INSERT] = {"INSERT", 0, NULL},
|
||||
[OPCODE_INSERT2] = {"INSERT2", 0, NULL},
|
||||
[OPCODE_PUSHINT] = {"PUSHINT", 1, (OperandType[]) {OPTP_INT}},
|
||||
[OPCODE_PUSHFLT] = {"PUSHFLT", 1, (OperandType[]) {OPTP_FLOAT}},
|
||||
[OPCODE_PUSHSTR] = {"PUSHSTR", 1, (OperandType[]) {OPTP_STRING}},
|
||||
[OPCODE_PUSHVAR] = {"PUSHVAR", 1, (OperandType[]) {OPTP_STRING}},
|
||||
[OPCODE_PUSHTRU] = {"PUSHTRU", 0, NULL},
|
||||
[OPCODE_PUSHFLS] = {"PUSHFLS", 0, NULL},
|
||||
[OPCODE_PUSHNNE] = {"PUSHNNE", 0, NULL},
|
||||
[OPCODE_PUSHFUN] = {"PUSHFUN", 2, (OperandType[]) {OPTP_INT, OPTP_INT}},
|
||||
[OPCODE_PUSHLST] = {"PUSHLST", 1, (OperandType[]) {OPTP_INT}},
|
||||
[OPCODE_PUSHMAP] = {"PUSHMAP", 1, (OperandType[]) {OPTP_INT}},
|
||||
|
||||
[OPCODE_RETURN] = {"RETURN", 1, (OperandType[]) {OPTP_INT}},
|
||||
|
||||
[OPCODE_JUMPIFNOTANDPOP] = {"JUMPIFNOTANDPOP", 1, (OperandType[]) {OPTP_INT}},
|
||||
[OPCODE_JUMPIFANDPOP] = {"JUMPIFANDPOP", 1, (OperandType[]) {OPTP_INT}},
|
||||
[OPCODE_JUMP] = {"JUMP", 1, (OperandType[]) {OPTP_INT}},
|
||||
};
|
||||
|
||||
_Bool Executable_GetOpcodeBinaryFromName(const char *name, size_t name_len, Opcode *opcode)
|
||||
{
|
||||
// The input name is assumed not zero-terminated,
|
||||
// so to simplify things we duplicate it to add
|
||||
// an extra null byte.
|
||||
char buff[128]; // No opcode should have a name this big.
|
||||
assert(name_len < sizeof(buff)); // If this is triggered, [buff] should be made bigger.
|
||||
|
||||
memcpy(buff, name, name_len);
|
||||
buff[name_len] = '\0';
|
||||
|
||||
name = buff;
|
||||
|
||||
// Now name is zero terminated.
|
||||
assert(name[name_len] == '\0');
|
||||
|
||||
for(size_t i = 0; i < sizeof(instr_table)/sizeof(instr_table[0]); i += 1) {
|
||||
if(!strcmp(name, instr_table[i].name)) {
|
||||
*opcode = i; // Is this safe?
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char *Executable_GetOpcodeName(Opcode opcode)
|
||||
{
|
||||
return instr_table[opcode].name;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
void Executable_Dump(Executable *exe)
|
||||
{
|
||||
for(int i = 0; i < exe->bodyl; i += 1)
|
||||
{
|
||||
Opcode opcode;
|
||||
Operand ops[MAX_OPS];
|
||||
int opc = MAX_OPS;
|
||||
|
||||
(void) Executable_Fetch(exe, i, &opcode, ops, &opc);
|
||||
|
||||
const InstrInfo *info = instr_table + exe->body[i].opcode;
|
||||
|
||||
fprintf(stderr, "%d: %s ", i, info->name);
|
||||
|
||||
for(int j = 0; j < opc; j += 1)
|
||||
{
|
||||
switch(ops[j].type)
|
||||
{
|
||||
case OPTP_INT:
|
||||
fprintf(stderr, "%lld ", ops[j].as_int);
|
||||
break;
|
||||
|
||||
case OPTP_FLOAT:
|
||||
fprintf(stderr, "%f ", ops[j].as_float);
|
||||
break;
|
||||
|
||||
case OPTP_STRING:
|
||||
fprintf(stderr, "[%s] ", ops[j].as_string);
|
||||
break;
|
||||
|
||||
case OPTP_PROMISE:
|
||||
UNREACHABLE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
}
|
||||
|
||||
_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;
|
||||
}
|
||||
|
||||
int Executable_GetInstrOffset(Executable *exe, int index)
|
||||
{
|
||||
if(index < 0 || index >= exe->bodyl)
|
||||
return -1;
|
||||
|
||||
if(exe->src)
|
||||
return exe->body[index].offset;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
int Executable_GetInstrLength(Executable *exe, int index)
|
||||
{
|
||||
if(index < 0 || index >= exe->bodyl)
|
||||
return -1;
|
||||
|
||||
if(exe->src)
|
||||
return exe->body[index].length;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
_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;
|
||||
}
|
||||
|
||||
_Bool Executable_Equiv(Executable *exe1, Executable *exe2, FILE *log, const char *log_prefix)
|
||||
{
|
||||
int idx = 0;
|
||||
while(1) {
|
||||
Operand exe1_opv[MAX_OPS];
|
||||
Operand exe2_opv[MAX_OPS];
|
||||
int exe1_opc = MAX_OPS;
|
||||
int exe2_opc = MAX_OPS;
|
||||
Opcode exe1_opcode;
|
||||
Opcode exe2_opcode;
|
||||
_Bool exe1_done = !Executable_Fetch(exe1, idx, &exe1_opcode, exe1_opv, &exe1_opc);
|
||||
_Bool exe2_done = !Executable_Fetch(exe2, idx, &exe2_opcode, exe2_opv, &exe2_opc);
|
||||
if(exe1_done != exe2_done) {
|
||||
if(log != NULL)
|
||||
fprintf(log, "%sExecutables have different sizes\n", log_prefix);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(exe1_done == true)
|
||||
break;
|
||||
|
||||
if(exe1_opcode != exe2_opcode) {
|
||||
if(log != NULL)
|
||||
fprintf(log, "%sInstructions at index %d have different opcodes (\"%s\" != \"%s\")\n", log_prefix, idx,
|
||||
Executable_GetOpcodeName(exe1_opcode), Executable_GetOpcodeName(exe2_opcode));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Since the instruction opcode is the
|
||||
// same, the number of operands must be
|
||||
// the same too. (Their type must be
|
||||
// the same too.)
|
||||
assert(exe1_opc == exe2_opc);
|
||||
|
||||
for(int opno = 0; opno < exe1_opc; opno += 1) {
|
||||
|
||||
assert(exe1_opv[opno].type == exe2_opv[opno].type);
|
||||
|
||||
// Also, an executable can never have
|
||||
// a promise operand. That's only used
|
||||
// when building the executable.
|
||||
assert(exe1_opv[opno].type != OPTP_PROMISE);
|
||||
|
||||
switch(exe1_opv[opno].type) {
|
||||
|
||||
case OPTP_INT:
|
||||
{
|
||||
int v1 = exe1_opv[opno].as_int;
|
||||
int v2 = exe2_opv[opno].as_int;
|
||||
if(v1 != v2) {
|
||||
if(log != NULL)
|
||||
fprintf(log, "%s%s Instructions (at index %d) have different integer operands (at index %d) (%d != %d)\n",
|
||||
log_prefix, Executable_GetOpcodeName(exe1_opcode), idx, opno, v1, v2);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case OPTP_FLOAT:
|
||||
{
|
||||
double v1 = exe1_opv[opno].as_float;
|
||||
double v2 = exe2_opv[opno].as_float;
|
||||
if(v1 != v2) {
|
||||
if(log != NULL)
|
||||
fprintf(log, "%s%s Instructions (at index %d) have different floating operands (at index %d) (%f != %f)\n",
|
||||
log_prefix, Executable_GetOpcodeName(exe1_opcode), idx, opno, v1, v2);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case OPTP_STRING:
|
||||
{
|
||||
const char *v1 = exe1_opv[opno].as_string;
|
||||
const char *v2 = exe2_opv[opno].as_string;
|
||||
if(strcmp(v1, v2)) {
|
||||
if(log != NULL)
|
||||
// TODO: Escape the strings before printing them.
|
||||
fprintf(log, "%s%s Instructions (at index %d) have different string operands (at index %d) (\"%s\" != \"%s\")\n",
|
||||
log_prefix, Executable_GetOpcodeName(exe1_opcode), idx, opno, v1, v2);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case OPTP_PROMISE:
|
||||
/* Unreachable */
|
||||
assert(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
idx += 1;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
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, 1, "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);
|
||||
|
||||
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 const unsigned int operand_type_sizes[] = {
|
||||
[OPTP_INT] = membersizeof(Operand, as_int),
|
||||
[OPTP_FLOAT] = membersizeof(Operand, as_float),
|
||||
[OPTP_STRING] = membersizeof(Operand, as_string),
|
||||
};
|
||||
|
||||
const InstrInfo *info = instr_table + opcode;
|
||||
|
||||
if(opc != info->opcount)
|
||||
{
|
||||
// ERROR: Too many operands were provided.
|
||||
Error_Report(error, 1,
|
||||
"Instruction %s expects %d operands, but %d were provided",
|
||||
info->name, info->opcount, opc);
|
||||
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, 1, "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, 1,
|
||||
"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, 1,
|
||||
"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;
|
||||
}
|
||||
|
||||
BPAlloc *ExeBuilder_GetAlloc(ExeBuilder *exeb)
|
||||
{
|
||||
return BucketList_GetAlloc(exeb->code);
|
||||
}
|
||||
|
||||
int ExeBuilder_InstrCount(ExeBuilder *exeb)
|
||||
{
|
||||
int raw_size = BucketList_Size(exeb->code);
|
||||
|
||||
assert(raw_size % sizeof(Instruction) == 0);
|
||||
|
||||
return raw_size / sizeof(Instruction);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
|
||||
/* +--------------------------------------------------------------------------+
|
||||
** | _ _ _ |
|
||||
** | | \ | | (_) |
|
||||
** | | \| | ___ _ __ _ |
|
||||
** | | . ` |/ _ \| |/ _` | |
|
||||
** | | |\ | (_) | | (_| | |
|
||||
** | |_| \_|\___/| |\__,_| |
|
||||
** | _/ | |
|
||||
** | |__/ |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | Copyright (c) 2022 Francesco Cozzuto <francesco.cozzuto@gmail.com> |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | This file is part of The Noja Interpreter. |
|
||||
** | |
|
||||
** | The Noja Interpreter is free software: you can redistribute it and/or |
|
||||
** | modify it under the terms of the GNU General Public License as published |
|
||||
** | by the Free Software Foundation, either version 3 of the License, or (at |
|
||||
** | your option) any later version. |
|
||||
** | |
|
||||
** | The Noja Interpreter is distributed in the hope that it will be useful, |
|
||||
** | but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||||
** | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General |
|
||||
** | Public License for more details. |
|
||||
** | |
|
||||
** | You should have received a copy of the GNU General Public License along |
|
||||
** | with The Noja Interpreter. If not, see <http://www.gnu.org/licenses/>. |
|
||||
** +--------------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#ifndef EXECUTABLE_H
|
||||
#define EXECUTABLE_H
|
||||
#include <stdio.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_NOT,
|
||||
OPCODE_ADD,
|
||||
OPCODE_SUB,
|
||||
OPCODE_MUL,
|
||||
OPCODE_DIV,
|
||||
OPCODE_EQL,
|
||||
OPCODE_NQL,
|
||||
OPCODE_LSS,
|
||||
OPCODE_GRT,
|
||||
OPCODE_LEQ,
|
||||
OPCODE_GEQ,
|
||||
OPCODE_AND,
|
||||
OPCODE_OR,
|
||||
OPCODE_ASS,
|
||||
OPCODE_POP,
|
||||
OPCODE_CALL,
|
||||
OPCODE_SELECT,
|
||||
OPCODE_INSERT,
|
||||
OPCODE_INSERT2,
|
||||
OPCODE_PUSHINT,
|
||||
OPCODE_PUSHFLT,
|
||||
OPCODE_PUSHSTR,
|
||||
OPCODE_PUSHVAR,
|
||||
OPCODE_PUSHTRU,
|
||||
OPCODE_PUSHFLS,
|
||||
OPCODE_PUSHNNE,
|
||||
OPCODE_PUSHFUN,
|
||||
OPCODE_PUSHLST,
|
||||
OPCODE_PUSHMAP,
|
||||
OPCODE_RETURN,
|
||||
OPCODE_JUMPIFANDPOP,
|
||||
OPCODE_JUMPIFNOTANDPOP,
|
||||
OPCODE_JUMP,
|
||||
} Opcode;
|
||||
|
||||
typedef struct xExecutable Executable;
|
||||
typedef struct xExeBuilder ExeBuilder;
|
||||
|
||||
Executable *Executable_Copy(Executable *exe);
|
||||
void Executable_Free(Executable *exe);
|
||||
void Executable_Dump(Executable *exe);
|
||||
_Bool Executable_Equiv(Executable *exe1, Executable *exe2, FILE *log, const char *log_prefix);
|
||||
_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);
|
||||
int Executable_GetInstrOffset(Executable *exe, int index);
|
||||
int Executable_GetInstrLength(Executable *exe, int index);
|
||||
const char *Executable_GetOpcodeName(Opcode opcode);
|
||||
_Bool Executable_GetOpcodeBinaryFromName(const char *name, size_t name_len, Opcode *opcode);
|
||||
|
||||
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);
|
||||
BPAlloc *ExeBuilder_GetAlloc(ExeBuilder *exeb);
|
||||
int ExeBuilder_InstrCount(ExeBuilder *exeb);
|
||||
#endif
|
||||
@@ -0,0 +1,34 @@
|
||||
|
||||
/* +--------------------------------------------------------------------------+
|
||||
** | _ _ _ |
|
||||
** | | \ | | (_) |
|
||||
** | | \| | ___ _ __ _ |
|
||||
** | | . ` |/ _ \| |/ _` | |
|
||||
** | | |\ | (_) | | (_| | |
|
||||
** | |_| \_|\___/| |\__,_| |
|
||||
** | _/ | |
|
||||
** | |__/ |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | Copyright (c) 2022 Francesco Cozzuto <francesco.cozzuto@gmail.com> |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | This file is part of The Noja Interpreter. |
|
||||
** | |
|
||||
** | The Noja Interpreter is free software: you can redistribute it and/or |
|
||||
** | modify it under the terms of the GNU General Public License as published |
|
||||
** | by the Free Software Foundation, either version 3 of the License, or (at |
|
||||
** | your option) any later version. |
|
||||
** | |
|
||||
** | The Noja Interpreter is distributed in the hope that it will be useful, |
|
||||
** | but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||||
** | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General |
|
||||
** | Public License for more details. |
|
||||
** | |
|
||||
** | You should have received a copy of the GNU General Public License along |
|
||||
** | with The Noja Interpreter. If not, see <http://www.gnu.org/licenses/>. |
|
||||
** +--------------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#ifndef AST_H
|
||||
#define AST_H
|
||||
typedef struct xAST AST;
|
||||
#endif
|
||||
@@ -0,0 +1,196 @@
|
||||
|
||||
/* +--------------------------------------------------------------------------+
|
||||
** | _ _ _ |
|
||||
** | | \ | | (_) |
|
||||
** | | \| | ___ _ __ _ |
|
||||
** | | . ` |/ _ \| |/ _` | |
|
||||
** | | |\ | (_) | | (_| | |
|
||||
** | |_| \_|\___/| |\__,_| |
|
||||
** | _/ | |
|
||||
** | |__/ |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | Copyright (c) 2022 Francesco Cozzuto <francesco.cozzuto@gmail.com> |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | This file is part of The Noja Interpreter. |
|
||||
** | |
|
||||
** | The Noja Interpreter is free software: you can redistribute it and/or |
|
||||
** | modify it under the terms of the GNU General Public License as published |
|
||||
** | by the Free Software Foundation, either version 3 of the License, or (at |
|
||||
** | your option) any later version. |
|
||||
** | |
|
||||
** | The Noja Interpreter is distributed in the hope that it will be useful, |
|
||||
** | but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||||
** | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General |
|
||||
** | Public License for more details. |
|
||||
** | |
|
||||
** | You should have received a copy of the GNU General Public License along |
|
||||
** | with The Noja Interpreter. If not, see <http://www.gnu.org/licenses/>. |
|
||||
** +--------------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#ifndef ASTi_H
|
||||
#define ASTi_H
|
||||
#include "../utils/source.h"
|
||||
#include "AST.h"
|
||||
|
||||
typedef enum {
|
||||
NODE_EXPR,
|
||||
NODE_IFELSE,
|
||||
NODE_COMP,
|
||||
NODE_RETURN,
|
||||
NODE_FUNC,
|
||||
NODE_ARG,
|
||||
NODE_WHILE,
|
||||
NODE_BREAK,
|
||||
NODE_DOWHILE,
|
||||
} NodeKind;
|
||||
|
||||
typedef enum {
|
||||
EXPR_POS,
|
||||
EXPR_NEG,
|
||||
EXPR_ADD,
|
||||
EXPR_SUB,
|
||||
EXPR_MUL,
|
||||
EXPR_DIV,
|
||||
|
||||
EXPR_EQL,
|
||||
EXPR_NQL,
|
||||
EXPR_LSS,
|
||||
EXPR_LEQ,
|
||||
EXPR_GRT,
|
||||
EXPR_GEQ,
|
||||
|
||||
EXPR_AND,
|
||||
EXPR_OR,
|
||||
EXPR_NOT,
|
||||
|
||||
EXPR_ASS,
|
||||
EXPR_INT,
|
||||
EXPR_MAP,
|
||||
EXPR_CALL,
|
||||
EXPR_PAIR,
|
||||
EXPR_LIST,
|
||||
EXPR_NONE,
|
||||
EXPR_TRUE,
|
||||
EXPR_FALSE,
|
||||
EXPR_FLOAT,
|
||||
EXPR_STRING,
|
||||
EXPR_IDENT,
|
||||
EXPR_SELECT,
|
||||
} ExprKind;
|
||||
|
||||
typedef struct Node Node;
|
||||
|
||||
struct xAST {
|
||||
Source *src;
|
||||
Node *root;
|
||||
};
|
||||
|
||||
struct Node {
|
||||
NodeKind kind;
|
||||
Node *next;
|
||||
int offset,
|
||||
length;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
Node base;
|
||||
ExprKind kind;
|
||||
} 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;
|
||||
Node *items;
|
||||
int itemc;
|
||||
} ListExprNode;
|
||||
|
||||
typedef struct {
|
||||
ExprNode base;
|
||||
Node *keys;
|
||||
Node *items;
|
||||
int itemc;
|
||||
} MapExprNode;
|
||||
|
||||
typedef struct {
|
||||
ExprNode base;
|
||||
char *val;
|
||||
int len;
|
||||
} IdentExprNode;
|
||||
|
||||
typedef struct {
|
||||
ExprNode base;
|
||||
Node *head;
|
||||
int count;
|
||||
} OperExprNode;
|
||||
|
||||
typedef struct {
|
||||
ExprNode base;
|
||||
Node *func;
|
||||
Node *argv;
|
||||
int argc;
|
||||
} CallExprNode;
|
||||
|
||||
typedef struct {
|
||||
ExprNode base;
|
||||
Node *idx;
|
||||
Node *set;
|
||||
} IndexSelectionExprNode;
|
||||
|
||||
typedef struct {
|
||||
Node base;
|
||||
Node *condition;
|
||||
Node *true_branch;
|
||||
Node *false_branch;
|
||||
} IfElseNode;
|
||||
|
||||
typedef struct {
|
||||
Node base;
|
||||
Node *condition;
|
||||
Node *body;
|
||||
} WhileNode;
|
||||
|
||||
typedef struct {
|
||||
Node base;
|
||||
Node *body;
|
||||
Node *condition;
|
||||
} DoWhileNode;
|
||||
|
||||
typedef struct {
|
||||
Node base;
|
||||
Node *head;
|
||||
} CompoundNode;
|
||||
|
||||
typedef struct {
|
||||
Node base;
|
||||
Node *val;
|
||||
} ReturnNode;
|
||||
|
||||
typedef struct {
|
||||
Node base;
|
||||
char *name;
|
||||
Node *argv;
|
||||
int argc;
|
||||
Node *body;
|
||||
} FunctionNode;
|
||||
|
||||
typedef struct {
|
||||
Node base;
|
||||
char *name;
|
||||
} ArgumentNode;
|
||||
#endif
|
||||
@@ -0,0 +1,771 @@
|
||||
|
||||
/* +--------------------------------------------------------------------------+
|
||||
** | _ _ _ |
|
||||
** | | \ | | (_) |
|
||||
** | | \| | ___ _ __ _ |
|
||||
** | | . ` |/ _ \| |/ _` | |
|
||||
** | | |\ | (_) | | (_| | |
|
||||
** | |_| \_|\___/| |\__,_| |
|
||||
** | _/ | |
|
||||
** | |__/ |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | Copyright (c) 2022 Francesco Cozzuto <francesco.cozzuto@gmail.com> |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | This file is part of The Noja Interpreter. |
|
||||
** | |
|
||||
** | The Noja Interpreter is free software: you can redistribute it and/or |
|
||||
** | modify it under the terms of the GNU General Public License as published |
|
||||
** | by the Free Software Foundation, either version 3 of the License, or (at |
|
||||
** | your option) any later version. |
|
||||
** | |
|
||||
** | The Noja Interpreter is distributed in the hope that it will be useful, |
|
||||
** | but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||||
** | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General |
|
||||
** | Public License for more details. |
|
||||
** | |
|
||||
** | You should have received a copy of the GNU General Public License along |
|
||||
** | with The Noja Interpreter. If not, see <http://www.gnu.org/licenses/>. |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | WHAT IS THIS FILE? |
|
||||
** | |
|
||||
** | This file implements the routines that transform the AST into a list of |
|
||||
** | bytecodes. The functionalities of this file are exposed through the |
|
||||
** | `compile` function, that takes as input an `AST` and outputs an |
|
||||
** | `Executable`. |
|
||||
** | |
|
||||
** | The function that does the heavy lifting is `emit_instr_for_node` which |
|
||||
** | walks the tree and writes instructions to the `ExeBuilder`. |
|
||||
** | |
|
||||
** | Some semantic errors are catched at this phase, in which case, they are |
|
||||
** | reported by filling out the `error` structure and aborting. It's also |
|
||||
** | possible that the compilation fails bacause of internal errors (which |
|
||||
** | usually means "out of memory"). |
|
||||
** +--------------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <setjmp.h>
|
||||
#include <stdlib.h>
|
||||
#include "../utils/defs.h"
|
||||
#include "compile.h"
|
||||
#include "ASTi.h"
|
||||
|
||||
static _Bool emit_instr_for_node(ExeBuilder *exeb, Node *node, Promise *break_dest, Error *error);
|
||||
|
||||
static Opcode exprkind_to_opcode(ExprKind kind)
|
||||
{
|
||||
switch(kind)
|
||||
{
|
||||
case EXPR_NOT: return OPCODE_NOT;
|
||||
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;
|
||||
case EXPR_EQL: return OPCODE_EQL;
|
||||
case EXPR_NQL: return OPCODE_NQL;
|
||||
case EXPR_LSS: return OPCODE_LSS;
|
||||
case EXPR_LEQ: return OPCODE_LEQ;
|
||||
case EXPR_GRT: return OPCODE_GRT;
|
||||
case EXPR_GEQ: return OPCODE_GEQ;
|
||||
case EXPR_AND: return OPCODE_AND;
|
||||
case EXPR_OR: return OPCODE_OR;
|
||||
default:
|
||||
UNREACHABLE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static _Bool
|
||||
emit_instr_for_funccall(ExeBuilder *exeb, CallExprNode *expr,
|
||||
Promise *break_dest, int returns, Error *error)
|
||||
{
|
||||
Node *arg = expr->argv;
|
||||
|
||||
while(arg)
|
||||
{
|
||||
if(!emit_instr_for_node(exeb, arg, break_dest, error))
|
||||
return 0;
|
||||
|
||||
arg = arg->next;
|
||||
}
|
||||
|
||||
if(!emit_instr_for_node(exeb, expr->func, break_dest, error))
|
||||
return 0;
|
||||
|
||||
Operand ops[2];
|
||||
ops[0] = (Operand) { .type = OPTP_INT, .as_int = expr->argc };
|
||||
ops[1] = (Operand) { .type = OPTP_INT, .as_int = returns };
|
||||
return ExeBuilder_Append(exeb, error, OPCODE_CALL, ops, 2, expr->base.base.offset, expr->base.base.length);
|
||||
}
|
||||
|
||||
static _Bool flatten_tuple_tree(ExprNode *root, ExprNode **tuple, int max, int *count, Error *error)
|
||||
{
|
||||
if(root->kind == EXPR_PAIR)
|
||||
return flatten_tuple_tree((ExprNode*) ((OperExprNode*) root)->head, tuple, max, count, error) &&
|
||||
flatten_tuple_tree((ExprNode*) ((OperExprNode*) root)->head->next, tuple, max, count, error);
|
||||
|
||||
if(max == *count)
|
||||
{
|
||||
Error_Report(error, 0, "Static buffer is too small");
|
||||
return 0;
|
||||
}
|
||||
|
||||
tuple[(*count)++] = root;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static _Bool emit_instr_for_node(ExeBuilder *exeb, Node *node, Promise *break_dest, Error *error)
|
||||
{
|
||||
assert(node != NULL);
|
||||
|
||||
switch(node->kind)
|
||||
{
|
||||
case NODE_EXPR:
|
||||
{
|
||||
ExprNode *expr = (ExprNode*) node;
|
||||
switch(expr->kind)
|
||||
{
|
||||
case EXPR_PAIR:
|
||||
Error_Report(error, 0, "Tuple outside of assignment or return statement");
|
||||
return 0;
|
||||
|
||||
case EXPR_NOT:
|
||||
case EXPR_POS:
|
||||
case EXPR_NEG:
|
||||
case EXPR_ADD:
|
||||
case EXPR_SUB:
|
||||
case EXPR_MUL:
|
||||
case EXPR_DIV:
|
||||
case EXPR_EQL:
|
||||
case EXPR_NQL:
|
||||
case EXPR_LSS:
|
||||
case EXPR_LEQ:
|
||||
case EXPR_GRT:
|
||||
case EXPR_GEQ:
|
||||
case EXPR_AND:
|
||||
case EXPR_OR:
|
||||
{
|
||||
OperExprNode *oper = (OperExprNode*) expr;
|
||||
|
||||
for(Node *operand = oper->head; operand; operand = operand->next)
|
||||
if(!emit_instr_for_node(exeb, operand, break_dest, error))
|
||||
return 0;
|
||||
|
||||
if(!ExeBuilder_Append(exeb, error,
|
||||
exprkind_to_opcode(expr->kind),
|
||||
NULL, 0, node->offset, node->length))
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
case EXPR_ASS:
|
||||
{
|
||||
OperExprNode *oper = (OperExprNode*) expr;
|
||||
|
||||
Node *lop, *rop;
|
||||
lop = oper->head;
|
||||
rop = lop->next;
|
||||
|
||||
ExprNode *tuple[32];
|
||||
int count = 0;
|
||||
|
||||
if(!flatten_tuple_tree((ExprNode*) lop, tuple, sizeof(tuple)/sizeof(tuple[0]), &count, error))
|
||||
return 0;
|
||||
|
||||
assert(count > 0);
|
||||
|
||||
if(count == 1) /* No tuple. */
|
||||
{
|
||||
if(!emit_instr_for_node(exeb, rop, break_dest, error))
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(((ExprNode*) rop)->kind == EXPR_CALL)
|
||||
{
|
||||
if(!emit_instr_for_funccall(exeb, (CallExprNode*) rop, break_dest, count, error))
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
Error_Report(error, 0, "Assigning to %d variables only 1 value", count);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
for(int i = 0; i < count; i += 1)
|
||||
{
|
||||
ExprNode *tuple_item = tuple[count-i-1];
|
||||
switch(tuple_item->kind)
|
||||
{
|
||||
case EXPR_IDENT:
|
||||
{
|
||||
const char *name = ((IdentExprNode*) tuple_item)->val;
|
||||
|
||||
Operand op = { .type = OPTP_STRING, .as_string = name };
|
||||
if(!ExeBuilder_Append(exeb, error, OPCODE_ASS, &op, 1, tuple_item->base.offset, tuple_item->base.length))
|
||||
return 0;
|
||||
break;
|
||||
}
|
||||
|
||||
case EXPR_SELECT:
|
||||
{
|
||||
Node *idx = ((IndexSelectionExprNode*) tuple_item)->idx;
|
||||
Node *set = ((IndexSelectionExprNode*) tuple_item)->set;
|
||||
|
||||
if(!emit_instr_for_node(exeb, set, break_dest, error))
|
||||
return 0;
|
||||
|
||||
if(!emit_instr_for_node(exeb, idx, break_dest, error))
|
||||
return 0;
|
||||
|
||||
if(!ExeBuilder_Append(exeb, error, OPCODE_INSERT2, NULL, 0, tuple_item->base.offset, tuple_item->base.length))
|
||||
return 0;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
Error_Report(error, 0, "Assigning to something that it can't be assigned to");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(i+1 < count)
|
||||
{
|
||||
Operand op = (Operand) { .type = OPTP_INT, .as_int = 1 };
|
||||
if(!ExeBuilder_Append(exeb, error, OPCODE_POP, &op, 1, node->offset, 0))
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
case EXPR_INT:
|
||||
{
|
||||
IntExprNode *p = (IntExprNode*) expr;
|
||||
Operand op = { .type = OPTP_INT, .as_int = p->val };
|
||||
return ExeBuilder_Append(exeb, error, OPCODE_PUSHINT, &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_PUSHFLT, &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_PUSHSTR, &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_PUSHVAR, &op, 1, node->offset, node->length);
|
||||
}
|
||||
|
||||
case EXPR_LIST:
|
||||
{
|
||||
// PUSHLST
|
||||
// PUSHINT
|
||||
// <expr>
|
||||
// INSERT
|
||||
|
||||
ListExprNode *l = (ListExprNode*) node;
|
||||
|
||||
Operand op;
|
||||
|
||||
op = (Operand) { .type = OPTP_INT, .as_int = l->itemc };
|
||||
if(!ExeBuilder_Append(exeb, error, OPCODE_PUSHLST, &op, 1, node->offset, node->length))
|
||||
return 0;
|
||||
|
||||
Node *item = l->items;
|
||||
int i = 0;
|
||||
|
||||
while(item)
|
||||
{
|
||||
op = (Operand) { .type = OPTP_INT, .as_int = i };
|
||||
if(!ExeBuilder_Append(exeb, error, OPCODE_PUSHINT, &op, 1, item->offset, item->length))
|
||||
return 0;
|
||||
|
||||
if(!emit_instr_for_node(exeb, item, break_dest, error))
|
||||
return 0;
|
||||
|
||||
if(!ExeBuilder_Append(exeb, error, OPCODE_INSERT, NULL, 0, item->offset, item->length))
|
||||
return 0;
|
||||
|
||||
i += 1;
|
||||
item = item->next;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
case EXPR_MAP:
|
||||
{
|
||||
MapExprNode *m = (MapExprNode*) node;
|
||||
|
||||
Operand op;
|
||||
|
||||
op = (Operand) { .type = OPTP_INT, .as_int = m->itemc };
|
||||
if(!ExeBuilder_Append(exeb, error, OPCODE_PUSHMAP, &op, 1, node->offset, node->length))
|
||||
return 0;
|
||||
|
||||
Node *key = m->keys;
|
||||
Node *item = m->items;
|
||||
|
||||
while(item)
|
||||
{
|
||||
if(!emit_instr_for_node(exeb, key, break_dest, error))
|
||||
return 0;
|
||||
|
||||
if(!emit_instr_for_node(exeb, item, break_dest, error))
|
||||
return 0;
|
||||
|
||||
if(!ExeBuilder_Append(exeb, error, OPCODE_INSERT, NULL, 0, item->offset, item->length))
|
||||
return 0;
|
||||
|
||||
key = key->next;
|
||||
item = item->next;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
case EXPR_CALL:
|
||||
return emit_instr_for_funccall(exeb, (CallExprNode*) expr, break_dest, 1, error);
|
||||
|
||||
case EXPR_SELECT:
|
||||
{
|
||||
IndexSelectionExprNode *sel = (IndexSelectionExprNode*) expr;
|
||||
|
||||
if(!emit_instr_for_node(exeb, sel->set, break_dest, error))
|
||||
return 0;
|
||||
|
||||
if(!emit_instr_for_node(exeb, sel->idx, break_dest, error))
|
||||
return 0;
|
||||
|
||||
return ExeBuilder_Append(exeb, error, OPCODE_SELECT, NULL, 0, node->offset, node->length);
|
||||
}
|
||||
|
||||
case EXPR_NONE:
|
||||
return ExeBuilder_Append(exeb, error, OPCODE_PUSHNNE, NULL, 0, node->offset, node->length);
|
||||
|
||||
case EXPR_TRUE:
|
||||
return ExeBuilder_Append(exeb, error, OPCODE_PUSHTRU, NULL, 0, node->offset, node->length);
|
||||
|
||||
case EXPR_FALSE:
|
||||
return ExeBuilder_Append(exeb, error, OPCODE_PUSHFLS, NULL, 0, node->offset, node->length);
|
||||
|
||||
default:
|
||||
UNREACHABLE;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case NODE_BREAK:
|
||||
{
|
||||
if(break_dest == NULL)
|
||||
{
|
||||
Error_Report(error, 0, "Break not inside a loop");
|
||||
return 0;
|
||||
}
|
||||
Operand op = (Operand) { .type = OPTP_PROMISE, .as_promise = break_dest };
|
||||
if(!ExeBuilder_Append(exeb, error, OPCODE_JUMP, &op, 1, node->offset, node->length))
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
case NODE_IFELSE:
|
||||
{
|
||||
IfElseNode *ifelse = (IfElseNode*) node;
|
||||
|
||||
if(!emit_instr_for_node(exeb, ifelse->condition, break_dest, error))
|
||||
return 0;
|
||||
|
||||
if(ifelse->false_branch)
|
||||
{
|
||||
Promise *else_offset = Promise_New(ExeBuilder_GetAlloc(exeb), sizeof(long long int));
|
||||
Promise *done_offset = Promise_New(ExeBuilder_GetAlloc(exeb), sizeof(long long int));
|
||||
|
||||
if(else_offset == NULL || done_offset == NULL)
|
||||
{
|
||||
Error_Report(error, 1, "No memory");
|
||||
return 0;
|
||||
}
|
||||
|
||||
Operand op = { .type = OPTP_PROMISE, .as_promise = else_offset };
|
||||
if(!ExeBuilder_Append(exeb, error, OPCODE_JUMPIFNOTANDPOP, &op, 1, node->offset, node->length))
|
||||
return 0;
|
||||
|
||||
if(!emit_instr_for_node(exeb, ifelse->true_branch, break_dest, error))
|
||||
return 0;
|
||||
|
||||
if(ifelse->true_branch->kind == NODE_EXPR)
|
||||
{
|
||||
Operand op = (Operand) { .type = OPTP_INT, .as_int = 1 };
|
||||
if(!ExeBuilder_Append(exeb, error, OPCODE_POP, &op, 1, ifelse->true_branch->offset, 0))
|
||||
return 0;
|
||||
}
|
||||
|
||||
op = (Operand) { .type = OPTP_PROMISE, .as_promise = done_offset };
|
||||
if(!ExeBuilder_Append(exeb, error, OPCODE_JUMP, &op, 1, node->offset, node->length))
|
||||
return 0;
|
||||
|
||||
long long int temp = ExeBuilder_InstrCount(exeb);
|
||||
Promise_Resolve(else_offset, &temp, sizeof(temp));
|
||||
|
||||
if(!emit_instr_for_node(exeb, ifelse->false_branch, break_dest, error))
|
||||
return 0;
|
||||
|
||||
if(ifelse->false_branch->kind == NODE_EXPR)
|
||||
{
|
||||
Operand op = (Operand) { .type = OPTP_INT, .as_int = 1 };
|
||||
if(!ExeBuilder_Append(exeb, error, OPCODE_POP, &op, 1, ifelse->false_branch->offset, 0))
|
||||
return 0;
|
||||
}
|
||||
|
||||
temp = ExeBuilder_InstrCount(exeb);
|
||||
Promise_Resolve(done_offset, &temp, sizeof(temp));
|
||||
|
||||
Promise_Free(else_offset);
|
||||
Promise_Free(done_offset);
|
||||
}
|
||||
else
|
||||
{
|
||||
Promise *done_offset = Promise_New(ExeBuilder_GetAlloc(exeb), sizeof(long long int));
|
||||
|
||||
if(done_offset == NULL)
|
||||
{
|
||||
Error_Report(error, 1, "No memory");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(!ExeBuilder_Append(exeb, error, OPCODE_JUMPIFNOTANDPOP, &(Operand) { .type = OPTP_PROMISE, .as_promise = done_offset }, 1, node->offset, node->length))
|
||||
return 0;
|
||||
|
||||
if(!emit_instr_for_node(exeb, ifelse->true_branch, break_dest, error))
|
||||
return 0;
|
||||
|
||||
if(ifelse->true_branch->kind == NODE_EXPR)
|
||||
{
|
||||
Operand op = (Operand) { .type = OPTP_INT, .as_int = 1 };
|
||||
if(!ExeBuilder_Append(exeb, error, OPCODE_POP, &op, 1, ifelse->true_branch->offset, 0))
|
||||
return 0;
|
||||
}
|
||||
|
||||
long long int temp = ExeBuilder_InstrCount(exeb);
|
||||
Promise_Resolve(done_offset, &temp, sizeof(temp));
|
||||
|
||||
Promise_Free(done_offset);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
case NODE_WHILE:
|
||||
{
|
||||
WhileNode *whl = (WhileNode*) node;
|
||||
|
||||
/*
|
||||
* start:
|
||||
* <condition>
|
||||
* JUMPIFNOTANDPOP end
|
||||
* <body>
|
||||
* JUMP start
|
||||
* end:
|
||||
*/
|
||||
|
||||
Promise *start_offset = Promise_New(ExeBuilder_GetAlloc(exeb), sizeof(long long int));
|
||||
Promise *end_offset = Promise_New(ExeBuilder_GetAlloc(exeb), sizeof(long long int));
|
||||
|
||||
if(start_offset == NULL || end_offset == NULL)
|
||||
{
|
||||
Error_Report(error, 1, "No memory");
|
||||
return 0;
|
||||
}
|
||||
|
||||
long long int temp = ExeBuilder_InstrCount(exeb);
|
||||
Promise_Resolve(start_offset, &temp, sizeof(temp));
|
||||
|
||||
if(!emit_instr_for_node(exeb, whl->condition, break_dest, error))
|
||||
return 0;
|
||||
|
||||
Operand op = { .type = OPTP_PROMISE, .as_promise = end_offset };
|
||||
if(!ExeBuilder_Append(exeb, error, OPCODE_JUMPIFNOTANDPOP, &op, 1, whl->condition->offset, whl->condition->length))
|
||||
return 0;
|
||||
|
||||
if(!emit_instr_for_node(exeb, whl->body, end_offset, error))
|
||||
return 0;
|
||||
|
||||
if(whl->body->kind == NODE_EXPR)
|
||||
{
|
||||
Operand op = (Operand) { .type = OPTP_INT, .as_int = 1 };
|
||||
if(!ExeBuilder_Append(exeb, error, OPCODE_POP, &op, 1, whl->body->offset, 0))
|
||||
return 0;
|
||||
}
|
||||
|
||||
op = (Operand) { .type = OPTP_PROMISE, .as_promise = start_offset };
|
||||
if(!ExeBuilder_Append(exeb, error, OPCODE_JUMP, &op, 1, node->offset, node->length))
|
||||
return 0;
|
||||
|
||||
temp = ExeBuilder_InstrCount(exeb);
|
||||
Promise_Resolve(end_offset, &temp, sizeof(temp));
|
||||
|
||||
Promise_Free(start_offset);
|
||||
Promise_Free( end_offset);
|
||||
return 1;
|
||||
}
|
||||
|
||||
case NODE_DOWHILE:
|
||||
{
|
||||
DoWhileNode *dowhl = (DoWhileNode*) node;
|
||||
|
||||
/*
|
||||
* start:
|
||||
* <body>
|
||||
* <condition>
|
||||
* JUMPIFANDPOP start
|
||||
*/
|
||||
|
||||
Promise *end_offset = Promise_New(ExeBuilder_GetAlloc(exeb), sizeof(long long int));
|
||||
if(end_offset == NULL)
|
||||
{
|
||||
Error_Report(error, 1, "No memory");
|
||||
return 0;
|
||||
}
|
||||
|
||||
long long int start = ExeBuilder_InstrCount(exeb);
|
||||
|
||||
if(!emit_instr_for_node(exeb, dowhl->body, end_offset, error))
|
||||
return 0;
|
||||
|
||||
if(dowhl->body->kind == NODE_EXPR)
|
||||
{
|
||||
Operand op = (Operand) { .type = OPTP_INT, .as_int = 1 };
|
||||
if(!ExeBuilder_Append(exeb, error, OPCODE_POP, &op, 1, dowhl->body->offset, 0))
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(!emit_instr_for_node(exeb, dowhl->condition, break_dest, error))
|
||||
return 0;
|
||||
|
||||
Operand op = { .type = OPTP_INT, .as_int = start };
|
||||
if(!ExeBuilder_Append(exeb, error, OPCODE_JUMPIFANDPOP, &op, 1, dowhl->condition->offset, dowhl->condition->length))
|
||||
return 0;
|
||||
|
||||
long long int temp = ExeBuilder_InstrCount(exeb);
|
||||
Promise_Resolve(end_offset, &temp, sizeof(temp));
|
||||
Promise_Free(end_offset);
|
||||
return 1;
|
||||
}
|
||||
|
||||
case NODE_COMP:
|
||||
{
|
||||
CompoundNode *comp = (CompoundNode*) node;
|
||||
|
||||
Node *stmt = comp->head;
|
||||
|
||||
while(stmt)
|
||||
{
|
||||
if(!emit_instr_for_node(exeb, stmt, break_dest, error))
|
||||
return 0;
|
||||
|
||||
if(stmt->kind == NODE_EXPR)
|
||||
{
|
||||
Operand op = (Operand) { .type = OPTP_INT, .as_int = 1 };
|
||||
if(!ExeBuilder_Append(exeb, error, OPCODE_POP, &op, 1, stmt->offset, 0))
|
||||
return 0;
|
||||
}
|
||||
stmt = stmt->next;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
case NODE_RETURN:
|
||||
{
|
||||
ReturnNode *ret = (ReturnNode*) node;
|
||||
|
||||
ExprNode *tuple[32];
|
||||
int count = 0;
|
||||
|
||||
if(!flatten_tuple_tree((ExprNode*) ret->val, tuple, sizeof(tuple)/sizeof(tuple[0]), &count, error))
|
||||
return 0;
|
||||
|
||||
for(int i = 0; i < count; i += 1)
|
||||
if(!emit_instr_for_node(exeb, (Node*) tuple[i], break_dest, error))
|
||||
return 0;
|
||||
|
||||
Operand op = (Operand) { .type = OPTP_INT, .as_int = count };
|
||||
if(!ExeBuilder_Append(exeb, error, OPCODE_RETURN, &op, 1, ret->base.offset, ret->base.length))
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
case NODE_FUNC:
|
||||
{
|
||||
FunctionNode *func = (FunctionNode*) node;
|
||||
|
||||
Promise *func_index = Promise_New(ExeBuilder_GetAlloc(exeb), sizeof(long long int));
|
||||
Promise *jump_index = Promise_New(ExeBuilder_GetAlloc(exeb), sizeof(long long int));
|
||||
|
||||
if(func_index == NULL || jump_index == NULL)
|
||||
{
|
||||
Error_Report(error, 1, "No memory");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Push function.
|
||||
{
|
||||
Operand ops[2] = {
|
||||
{ .type = OPTP_PROMISE, .as_promise = func_index },
|
||||
{ .type = OPTP_INT, .as_int = func->argc },
|
||||
};
|
||||
|
||||
if(!ExeBuilder_Append(exeb, error, OPCODE_PUSHFUN, ops, 2, func->base.offset, func->base.length))
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Assign variable.
|
||||
Operand op = (Operand) { .type = OPTP_STRING, .as_string = func->name };
|
||||
if(!ExeBuilder_Append(exeb, error, OPCODE_ASS, &op, 1, func->base.offset, func->base.length))
|
||||
return 0;
|
||||
|
||||
// Pop function object.
|
||||
op = (Operand) { .type = OPTP_INT, .as_int = 1 };
|
||||
if(!ExeBuilder_Append(exeb, error, OPCODE_POP, &op, 1, func->base.offset, func->base.length))
|
||||
return 0;
|
||||
|
||||
// Jump after the function code.
|
||||
op = (Operand) { .type = OPTP_PROMISE, .as_promise = jump_index };
|
||||
if(!ExeBuilder_Append(exeb, error, OPCODE_JUMP, &op, 1, func->base.offset, func->base.length))
|
||||
return 0;
|
||||
|
||||
// This is the function code index.
|
||||
long long int temp = ExeBuilder_InstrCount(exeb);
|
||||
Promise_Resolve(func_index, &temp, sizeof(temp));
|
||||
|
||||
// Compile the function body.
|
||||
{
|
||||
// Assign the arguments.
|
||||
|
||||
if(func->argv)
|
||||
{ assert(func->argv->kind == NODE_ARG); }
|
||||
|
||||
ArgumentNode *arg = (ArgumentNode*) func->argv;
|
||||
|
||||
while(arg)
|
||||
{
|
||||
op = (Operand) { .type = OPTP_STRING, .as_string = arg->name };
|
||||
if(!ExeBuilder_Append(exeb, error, OPCODE_ASS, &op, 1, arg->base.offset, arg->base.length))
|
||||
return 0;
|
||||
|
||||
op = (Operand) { .type = OPTP_INT, .as_int = 1 };
|
||||
if(!ExeBuilder_Append(exeb, error, OPCODE_POP, &op, 1, arg->base.offset, arg->base.length))
|
||||
return 0;
|
||||
|
||||
if(arg->base.next)
|
||||
{ assert(arg->base.next->kind == NODE_ARG); }
|
||||
|
||||
arg = (ArgumentNode*) arg->base.next;
|
||||
}
|
||||
|
||||
if(!emit_instr_for_node(exeb, func->body, NULL, error))
|
||||
return 0;
|
||||
|
||||
if(func->body->kind == NODE_EXPR)
|
||||
{
|
||||
Operand op = (Operand) { .type = OPTP_INT, .as_int = 1 };
|
||||
if(!ExeBuilder_Append(exeb, error, OPCODE_POP, &op, 1, func->body->offset + func->body->length, 0))
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Write a return instruction, just
|
||||
// in case it didn't already return.
|
||||
Operand op = (Operand) { .type = OPTP_INT, .as_int = 0 };
|
||||
if(!ExeBuilder_Append(exeb, error, OPCODE_RETURN, &op, 1, func->body->offset, 0))
|
||||
return 0;
|
||||
}
|
||||
|
||||
// This is the first index after the function code.
|
||||
temp = ExeBuilder_InstrCount(exeb);
|
||||
Promise_Resolve(jump_index, &temp, sizeof(temp));
|
||||
|
||||
Promise_Free(func_index);
|
||||
Promise_Free(jump_index);
|
||||
return 1;
|
||||
}
|
||||
|
||||
default:
|
||||
UNREACHABLE;
|
||||
return 0;
|
||||
}
|
||||
UNREACHABLE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Symbol: compile
|
||||
*
|
||||
* Serializes an AST into bytecode format.
|
||||
*
|
||||
*
|
||||
* Arguments:
|
||||
*
|
||||
* ast: The AST to be serialized.
|
||||
* alloc: The allocator that will be used to get new
|
||||
* memory. (optional)
|
||||
* error: Error information structure that is filled out if
|
||||
* an error occurres.
|
||||
*
|
||||
*
|
||||
* Returns:
|
||||
* A pointer to an `Executable` that is the object that
|
||||
* contains the bytecode. If an error occurres, NULL is
|
||||
* returned and the `error` structure is filled out.
|
||||
*
|
||||
*/
|
||||
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 != NULL)
|
||||
{
|
||||
if(!emit_instr_for_node(exeb, ast->root, NULL, error))
|
||||
return 0;
|
||||
|
||||
Operand op = (Operand) { .type = OPTP_INT, .as_int = 0 };
|
||||
if(ExeBuilder_Append(exeb, error, OPCODE_RETURN, &op, 1, Source_GetSize(ast->src), 0))
|
||||
{
|
||||
exe = ExeBuilder_Finalize(exeb, error);
|
||||
|
||||
if(exe != NULL)
|
||||
Executable_SetSource(exe, ast->src);
|
||||
}
|
||||
}
|
||||
|
||||
if(alloc == NULL)
|
||||
BPAlloc_Free(alloc2);
|
||||
return exe;
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
|
||||
/* +--------------------------------------------------------------------------+
|
||||
** | _ _ _ |
|
||||
** | | \ | | (_) |
|
||||
** | | \| | ___ _ __ _ |
|
||||
** | | . ` |/ _ \| |/ _` | |
|
||||
** | | |\ | (_) | | (_| | |
|
||||
** | |_| \_|\___/| |\__,_| |
|
||||
** | _/ | |
|
||||
** | |__/ |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | Copyright (c) 2022 Francesco Cozzuto <francesco.cozzuto@gmail.com> |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | This file is part of The Noja Interpreter. |
|
||||
** | |
|
||||
** | The Noja Interpreter is free software: you can redistribute it and/or |
|
||||
** | modify it under the terms of the GNU General Public License as published |
|
||||
** | by the Free Software Foundation, either version 3 of the License, or (at |
|
||||
** | your option) any later version. |
|
||||
** | |
|
||||
** | The Noja Interpreter is distributed in the hope that it will be useful, |
|
||||
** | but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||||
** | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General |
|
||||
** | Public License for more details. |
|
||||
** | |
|
||||
** | You should have received a copy of the GNU General Public License along |
|
||||
** | with The Noja Interpreter. If not, see <http://www.gnu.org/licenses/>. |
|
||||
** +--------------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#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
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,38 @@
|
||||
|
||||
/* +--------------------------------------------------------------------------+
|
||||
** | _ _ _ |
|
||||
** | | \ | | (_) |
|
||||
** | | \| | ___ _ __ _ |
|
||||
** | | . ` |/ _ \| |/ _` | |
|
||||
** | | |\ | (_) | | (_| | |
|
||||
** | |_| \_|\___/| |\__,_| |
|
||||
** | _/ | |
|
||||
** | |__/ |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | Copyright (c) 2022 Francesco Cozzuto <francesco.cozzuto@gmail.com> |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | This file is part of The Noja Interpreter. |
|
||||
** | |
|
||||
** | The Noja Interpreter is free software: you can redistribute it and/or |
|
||||
** | modify it under the terms of the GNU General Public License as published |
|
||||
** | by the Free Software Foundation, either version 3 of the License, or (at |
|
||||
** | your option) any later version. |
|
||||
** | |
|
||||
** | The Noja Interpreter is distributed in the hope that it will be useful, |
|
||||
** | but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||||
** | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General |
|
||||
** | Public License for more details. |
|
||||
** | |
|
||||
** | You should have received a copy of the GNU General Public License along |
|
||||
** | with The Noja Interpreter. If not, see <http://www.gnu.org/licenses/>. |
|
||||
** +--------------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#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
|
||||
+340
@@ -0,0 +1,340 @@
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include "utils/error.h"
|
||||
#include "utils/source.h"
|
||||
#include "compiler/parse.h"
|
||||
#include "compiler/compile.h"
|
||||
#include "assembler/assemble.h"
|
||||
#include "builtins/basic.h"
|
||||
#include "noja.h"
|
||||
|
||||
static void print_error(const char *type, Error *error)
|
||||
{
|
||||
if(type == NULL)
|
||||
fprintf(stderr, "Error");
|
||||
else if(error->internal)
|
||||
fprintf(stderr, "Internal Error");
|
||||
else
|
||||
fprintf(stderr, "%s Error", type);
|
||||
|
||||
fprintf(stderr, ": %s.", error->message);
|
||||
|
||||
#ifdef DEBUG
|
||||
if(error->file != NULL)
|
||||
{
|
||||
if(error->line > 0 && error->func != NULL)
|
||||
fprintf(stderr, " (Reported in %s:%d in %s)", error->file, error->line, error->func);
|
||||
else if(error->line > 0 && error->func == NULL)
|
||||
fprintf(stderr, " (Reported in %s:%d)", error->file, error->line);
|
||||
else if(error->line < 1 && error->func != NULL)
|
||||
fprintf(stderr, " (Reported in %s in %s)", error->file, error->func);
|
||||
}
|
||||
#endif
|
||||
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
|
||||
static Executable *build(Source *src)
|
||||
{
|
||||
Executable *exe;
|
||||
|
||||
// Create a bump-pointer allocator to hold the AST.
|
||||
BPAlloc *alloc = BPAlloc_Init(-1);
|
||||
|
||||
if(alloc == NULL)
|
||||
{
|
||||
fprintf(stderr, "Internal Error: Couldn't allocate bump-pointer allocator to hold the AST.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
Error error;
|
||||
Error_Init(&error);
|
||||
|
||||
// NOTE: The AST is stored in the BPAlloc. It's
|
||||
// lifetime is the same as the pool.
|
||||
AST *ast = parse(src, alloc, &error);
|
||||
|
||||
if(ast == NULL)
|
||||
{
|
||||
assert(error.occurred);
|
||||
print_error("Parsing", &error);
|
||||
Error_Free(&error);
|
||||
BPAlloc_Free(alloc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
exe = compile(ast, alloc, &error);
|
||||
|
||||
// We're done with the AST, independently from
|
||||
// the compilation result.
|
||||
BPAlloc_Free(alloc);
|
||||
|
||||
if(exe == NULL)
|
||||
{
|
||||
assert(error.occurred);
|
||||
print_error("Compilation", &error);
|
||||
Error_Free(&error);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return exe;
|
||||
}
|
||||
|
||||
static _Bool interpret(Executable *exe)
|
||||
{
|
||||
Runtime *runt = Runtime_New(-1, 1024*1024, NULL, NULL);
|
||||
|
||||
if(runt == NULL)
|
||||
{
|
||||
Error error;
|
||||
Error_Init(&error);
|
||||
Error_Report(&error, 1, "Couldn't initialize runtime");
|
||||
print_error(NULL, &error);
|
||||
Error_Free(&error);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// We use a [RuntimeError] instead of a simple [Error]
|
||||
// because the [RuntimeError] makes a snapshot of the
|
||||
// runtime state when an error is reported. Other than
|
||||
// this fact they are interchangable. Any function that
|
||||
// expects a pointer to [Error] can receive a [RuntimeError]
|
||||
// upcasted to [Error].
|
||||
RuntimeError error;
|
||||
RuntimeError_Init(&error, runt); // Here we specify the runtime to snapshot in case of failure.
|
||||
|
||||
Object *bins = Object_NewStaticMap(bins_basic, runt, (Error*) &error);
|
||||
|
||||
if(bins == NULL)
|
||||
{
|
||||
assert(error.base.occurred == 1);
|
||||
print_error(NULL, (Error*) &error);
|
||||
RuntimeError_Free(&error);
|
||||
Runtime_Free(runt);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Runtime_SetBuiltins(runt, bins);
|
||||
|
||||
Object *rets[8];
|
||||
unsigned int maxretc = sizeof(rets)/sizeof(rets[0]);
|
||||
|
||||
int retc = run(runt, (Error*) &error, exe, 0, NULL, NULL, 0, rets, maxretc);
|
||||
|
||||
// NOTE: The pointer to the builtins object is invalidated
|
||||
// now because it may be moved by the garbage collector.
|
||||
|
||||
if(retc < 0)
|
||||
{
|
||||
print_error("Runtime", (Error*) &error);
|
||||
|
||||
if(error.snapshot == NULL)
|
||||
fprintf(stderr, "No snapshot available.\n");
|
||||
else
|
||||
Snapshot_Print(error.snapshot, stderr);
|
||||
|
||||
RuntimeError_Free(&error);
|
||||
}
|
||||
|
||||
Runtime_Free(runt);
|
||||
return retc > -1;
|
||||
}
|
||||
|
||||
static _Bool disassemble(Source *src)
|
||||
{
|
||||
Executable *exe = build(src);
|
||||
|
||||
if(exe == NULL)
|
||||
return 0;
|
||||
|
||||
Executable_Dump(exe);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static _Bool interpret_file(const char *file)
|
||||
{
|
||||
Error error;
|
||||
Error_Init(&error);
|
||||
|
||||
Source *src = Source_FromFile(file, &error);
|
||||
|
||||
if(src == NULL)
|
||||
{
|
||||
assert(error.occurred == 1);
|
||||
print_error(NULL, &error);
|
||||
Error_Free(&error);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Executable *exe = build(src);
|
||||
|
||||
if(exe == NULL)
|
||||
return 0;
|
||||
|
||||
_Bool r = interpret(exe);
|
||||
|
||||
Executable_Free(exe);
|
||||
Source_Free(src);
|
||||
return r;
|
||||
}
|
||||
|
||||
static _Bool interpret_code(const char *code)
|
||||
{
|
||||
Error error;
|
||||
Error_Init(&error);
|
||||
|
||||
Source *src = Source_FromString(NULL, code, -1, &error);
|
||||
|
||||
if(src == NULL)
|
||||
{
|
||||
assert(error.occurred);
|
||||
print_error(NULL, &error);
|
||||
Error_Free(&error);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Executable *exe = build(src);
|
||||
|
||||
if(exe == NULL)
|
||||
return 0;
|
||||
|
||||
_Bool r = interpret(exe);
|
||||
|
||||
Executable_Free(exe);
|
||||
Source_Free(src);
|
||||
return r;
|
||||
}
|
||||
|
||||
static _Bool interpret_asm_file(const char *file)
|
||||
{
|
||||
Error error;
|
||||
Error_Init(&error);
|
||||
|
||||
Source *src = Source_FromFile(file, &error);
|
||||
|
||||
if(src == NULL)
|
||||
{
|
||||
assert(error.occurred == 1);
|
||||
print_error(NULL, &error);
|
||||
Error_Free(&error);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Executable *exe = assemble(src, &error);
|
||||
if(exe == NULL) {
|
||||
print_error("Assemblation", &error);
|
||||
Source_Free(src);
|
||||
Error_Free(&error);
|
||||
return 0;
|
||||
}
|
||||
|
||||
_Bool r = interpret(exe);
|
||||
|
||||
Executable_Free(exe);
|
||||
Source_Free(src);
|
||||
Error_Free(&error);
|
||||
return r;
|
||||
}
|
||||
|
||||
static _Bool interpret_asm_code(const char *code)
|
||||
{
|
||||
Error error;
|
||||
Error_Init(&error);
|
||||
|
||||
Source *src = Source_FromString(NULL, code, -1, &error);
|
||||
|
||||
if(src == NULL)
|
||||
{
|
||||
assert(error.occurred);
|
||||
print_error(NULL, &error);
|
||||
Error_Free(&error);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Executable *exe = assemble(src, &error);
|
||||
if(exe == NULL) {
|
||||
print_error("Assemblation", &error);
|
||||
Source_Free(src);
|
||||
Error_Free(&error);
|
||||
return 0;
|
||||
}
|
||||
|
||||
_Bool r = interpret(exe);
|
||||
|
||||
Executable_Free(exe);
|
||||
Source_Free(src);
|
||||
Error_Free(&error);
|
||||
return r;
|
||||
}
|
||||
|
||||
static _Bool disassemble_file(const char *file)
|
||||
{
|
||||
Error error;
|
||||
Error_Init(&error);
|
||||
|
||||
Source *src = Source_FromFile(file, &error);
|
||||
|
||||
if(src == NULL)
|
||||
{
|
||||
assert(error.occurred == 1);
|
||||
print_error(NULL, &error);
|
||||
Error_Free(&error);
|
||||
return 0;
|
||||
}
|
||||
|
||||
_Bool r = disassemble(src);
|
||||
|
||||
Source_Free(src);
|
||||
return r;
|
||||
}
|
||||
|
||||
static _Bool disassemble_code(const char *code)
|
||||
{
|
||||
Error error;
|
||||
Error_Init(&error);
|
||||
|
||||
Source *src = Source_FromString(NULL, code, -1, &error);
|
||||
|
||||
if(src == NULL)
|
||||
{
|
||||
assert(error.occurred);
|
||||
print_error(NULL, &error);
|
||||
Error_Free(&error);
|
||||
return 0;
|
||||
}
|
||||
|
||||
_Bool r = disassemble(src);
|
||||
|
||||
Source_Free(src);
|
||||
return r;
|
||||
}
|
||||
|
||||
_Bool NOJA_runString(const char *str)
|
||||
{
|
||||
return interpret_code(str);
|
||||
}
|
||||
|
||||
_Bool NOJA_runFile(const char *file)
|
||||
{
|
||||
return interpret_file(file);
|
||||
}
|
||||
|
||||
_Bool NOJA_dumpFileBytecode(const char *file)
|
||||
{
|
||||
return disassemble_file(file);
|
||||
}
|
||||
|
||||
_Bool NOJA_dumpStringBytecode(const char *str)
|
||||
{
|
||||
return disassemble_code(str);
|
||||
}
|
||||
|
||||
_Bool NOJA_runAssemblyFile(const char *file)
|
||||
{
|
||||
return interpret_asm_file(file);
|
||||
}
|
||||
|
||||
_Bool NOJA_runAssemblyString(const char *str)
|
||||
{
|
||||
return interpret_asm_code(str);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
#ifndef NOJA_H
|
||||
#define NOJA_H
|
||||
_Bool NOJA_runFile (const char *file);
|
||||
_Bool NOJA_runString(const char *str);
|
||||
_Bool NOJA_runAssemblyFile(const char *file);
|
||||
_Bool NOJA_runAssemblyString(const char *str);
|
||||
_Bool NOJA_dumpFileBytecode(const char *file);
|
||||
_Bool NOJA_dumpStringBytecode(const char *str);
|
||||
#endif /* NOJA_H */
|
||||
@@ -0,0 +1,8 @@
|
||||
|
||||
typedef struct NOJA_Executable NOJA_Executable;
|
||||
char *NOJA_disassemble(NOJA_Executable *exe);
|
||||
NOJA_Executable *NOJA_assembleString(const char *str);
|
||||
NOJA_Executable *NOJA_assembleFile(const char *file);
|
||||
NOJA_Executable *NOJA_compileString(const char *str);
|
||||
NOJA_Executable *NOJA_compileFile(const char *file);
|
||||
bool NOJA_compareExecutables(NOJA_Executable *exe1, NOJA_Executable *exe2);
|
||||
@@ -0,0 +1,524 @@
|
||||
|
||||
/* +--------------------------------------------------------------------------+
|
||||
** | _ _ _ |
|
||||
** | | \ | | (_) |
|
||||
** | | \| | ___ _ __ _ |
|
||||
** | | . ` |/ _ \| |/ _` | |
|
||||
** | | |\ | (_) | | (_| | |
|
||||
** | |_| \_|\___/| |\__,_| |
|
||||
** | _/ | |
|
||||
** | |__/ |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | Copyright (c) 2022 Francesco Cozzuto <francesco.cozzuto@gmail.com> |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | This file is part of The Noja Interpreter. |
|
||||
** | |
|
||||
** | The Noja Interpreter is free software: you can redistribute it and/or |
|
||||
** | modify it under the terms of the GNU General Public License as published |
|
||||
** | by the Free Software Foundation, either version 3 of the License, or (at |
|
||||
** | your option) any later version. |
|
||||
** | |
|
||||
** | The Noja Interpreter is distributed in the hope that it will be useful, |
|
||||
** | but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||||
** | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General |
|
||||
** | Public License for more details. |
|
||||
** | |
|
||||
** | You should have received a copy of the GNU General Public License along |
|
||||
** | with The Noja Interpreter. If not, see <http://www.gnu.org/licenses/>. |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | |
|
||||
** | WHAT IS THIS FILE? |
|
||||
** | This is the implementation of the "Heap", an object that provides the |
|
||||
** | rest of the program with memory and manages it by claiming it back |
|
||||
** | implicitly when it's not in use anymore. To determine which memory is |
|
||||
** | used or not, the heap system must be aware of the object graph. This is |
|
||||
** | the reason why the Heap is tightly coupled to the object model. |
|
||||
** | |
|
||||
** | HOW DOES IT WORK? |
|
||||
** | The collection algorithm is move-and-compact. The allocator is a |
|
||||
** | bump-pointer allocator. When the base pool of memory is filled up, |
|
||||
** | further allocations are forwarded to the stdlib's malloc, but are kept |
|
||||
** | track of by putting them in a linked list. When the language's runtime |
|
||||
** | system decides to free up some memory, a new heap is allocated and the |
|
||||
** | live objects are moved to it, then the old heap is freed. The references |
|
||||
** | between live objects are updated when moving them. Some objects implement|
|
||||
** | destructors that must be called when a new heap is allocated and they're |
|
||||
** | not moved to it. An auxiliary list of allocated objects with destructors |
|
||||
** | is stored alongside the heap. When the live objects are moved and the |
|
||||
** | ones to be destroyed are left in the old one, the list of objects with |
|
||||
** | destructors is iterated over and the objects in it that weren't moved are|
|
||||
** | destroied and removed from the list. This approach becomes linearly |
|
||||
** | slower with the number of allocated objects with destructors, but it's |
|
||||
** | assumed that not many of them implement them. |
|
||||
** | If during a collection the new memory pool is filled up, then an error is|
|
||||
** | thrown to the parent system. |
|
||||
** | |
|
||||
** | HOW ARE POINTERS UPDATED? |
|
||||
** | Basically, when an object is moved from the old to the new heap, the |
|
||||
** | location of the object in the old heap is overwritten with a placeholder |
|
||||
** | object that holds the new location. Then all of it's references are |
|
||||
** | iterated over and if they refer to placeholders they're updated with the |
|
||||
** | new location of the object. If the references don't refer to placeholder |
|
||||
** | objects, then the referred objects are moved too. This is a recursive |
|
||||
** | process that, when applied to the root object of the program, moves all |
|
||||
** | reachable objects to the new heap and updates the pointers. The |
|
||||
** | complexity of this algorithm is proportional to the number of live |
|
||||
** | objects. |
|
||||
** | |
|
||||
** | WHAT IS A BUMP-POINTER ALLOCATOR? |
|
||||
** | A bump-pointer allocator is a minimal memory management system. A |
|
||||
** | contiguous pool of memory is allocated. On a higher level, allocations |
|
||||
** | are stacked one after another until the pool is all used up. This is done|
|
||||
** | by having a pointer that points to the first free buffer of the pool. |
|
||||
** | Initially, it points to the first byte of the pool. When N bytes are |
|
||||
** | requested, the value of the pointer is given to the caller and then it's |
|
||||
** | incremented by the allocated amount. When the pool has less free memory |
|
||||
** | than what is requested, the allocation fails. |
|
||||
** +--------------------------------------------------------------------------+
|
||||
*/
|
||||
#include <stdint.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include "objects.h"
|
||||
|
||||
#if USING_VALGRIND
|
||||
#include <valgrind/memcheck.h>
|
||||
#endif
|
||||
|
||||
typedef struct OflowAlloc OflowAlloc;
|
||||
struct OflowAlloc {
|
||||
OflowAlloc *prev;
|
||||
char body[];
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
Object *object;
|
||||
_Bool (*destructor)(Object*, Error*);
|
||||
} PendingDestruct;
|
||||
|
||||
struct xHeap {
|
||||
int objcount;
|
||||
int size;
|
||||
int used;
|
||||
int total;
|
||||
void *body;
|
||||
OflowAlloc *oflow;
|
||||
PendingDestruct *pend;
|
||||
int pend_size, pend_used;
|
||||
|
||||
_Bool collecting;
|
||||
_Bool collection_failed;
|
||||
int movedcount;
|
||||
void *old_body;
|
||||
int old_used;
|
||||
int old_total;
|
||||
OflowAlloc *old_oflow;
|
||||
Error *error;
|
||||
};
|
||||
|
||||
Heap *Heap_New(int size)
|
||||
{
|
||||
if(size < 0)
|
||||
size = 65536;
|
||||
|
||||
Heap *heap = malloc(sizeof(Heap));
|
||||
|
||||
if(heap == NULL)
|
||||
return NULL;
|
||||
|
||||
heap->objcount = 0;
|
||||
heap->total = 0;
|
||||
heap->size = size;
|
||||
heap->used = 0;
|
||||
heap->body = malloc(size);
|
||||
heap->pend = NULL;
|
||||
heap->pend_size = 0;
|
||||
heap->pend_used = 0;
|
||||
heap->oflow = 0;
|
||||
heap->collecting = 0;
|
||||
|
||||
if(heap->body == NULL)
|
||||
{
|
||||
free(heap);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#if USING_VALGRIND
|
||||
VALGRIND_CREATE_MEMPOOL(heap, 0, 0);
|
||||
#endif
|
||||
|
||||
return heap;
|
||||
}
|
||||
|
||||
void Heap_Free(Heap *heap)
|
||||
{
|
||||
|
||||
#if USING_VALGRIND
|
||||
VALGRIND_DESTROY_MEMPOOL(heap);
|
||||
#endif
|
||||
|
||||
Error error;
|
||||
Error_Init(&error);
|
||||
|
||||
for(int i = 0; i < heap->pend_used; i += 1)
|
||||
{
|
||||
heap->pend[i].destructor(heap->pend[i].object, &error);
|
||||
if(error.occurred)
|
||||
{
|
||||
// Errors occurred! We can't do anything about
|
||||
// it now though.
|
||||
Error_Free(&error);
|
||||
Error_Init(&error);
|
||||
}
|
||||
}
|
||||
|
||||
while(heap->oflow)
|
||||
{
|
||||
OflowAlloc *prev = heap->oflow->prev;
|
||||
free(heap->oflow);
|
||||
heap->oflow = prev;
|
||||
}
|
||||
|
||||
free(heap->pend);
|
||||
free(heap->body);
|
||||
free(heap);
|
||||
}
|
||||
|
||||
void *Heap_GetPointer(Heap *heap)
|
||||
{
|
||||
return heap->body;
|
||||
}
|
||||
|
||||
unsigned int Heap_GetSize(Heap *heap)
|
||||
{
|
||||
return heap->size;
|
||||
}
|
||||
|
||||
unsigned int Heap_GetObjectCount(Heap *heap)
|
||||
{
|
||||
return heap->objcount;
|
||||
}
|
||||
|
||||
float Heap_GetUsagePercentage(Heap *heap)
|
||||
{
|
||||
return 100.0 * heap->total / heap->size;
|
||||
}
|
||||
|
||||
void *Heap_Malloc(Heap *heap, TypeObject *type, Error *err)
|
||||
{
|
||||
_Bool requires_destruct = type->free != NULL;
|
||||
|
||||
if(requires_destruct)
|
||||
{
|
||||
// This type of object requires
|
||||
// a destructor to be called.
|
||||
if(heap->pend == NULL)
|
||||
{
|
||||
int n = 8;
|
||||
|
||||
heap->pend = malloc(n * sizeof(PendingDestruct));
|
||||
|
||||
if(heap->pend == NULL)
|
||||
{
|
||||
Error_Report(err, 1, "No memory");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
heap->pend_used = 0;
|
||||
heap->pend_size = n;
|
||||
}
|
||||
else if(heap->pend_size == heap->pend_used)
|
||||
{
|
||||
int factor = 2;
|
||||
|
||||
void *new_pend = realloc(heap->pend, factor * heap->pend_size * sizeof(PendingDestruct));
|
||||
|
||||
if(new_pend == NULL)
|
||||
{
|
||||
Error_Report(err, 1, "No memory");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
heap->pend = new_pend;
|
||||
heap->pend_size *= factor;
|
||||
}
|
||||
|
||||
assert(heap->pend_size > heap->pend_used);
|
||||
}
|
||||
|
||||
int size = type->size;
|
||||
|
||||
if(size < (int) sizeof(MovedObject))
|
||||
size = sizeof(MovedObject);
|
||||
|
||||
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;
|
||||
|
||||
if(requires_destruct)
|
||||
heap->pend[heap->pend_used++] = (PendingDestruct) { .object = obj, .destructor = obj->type->free };
|
||||
|
||||
heap->objcount += 1;
|
||||
|
||||
return (Object*) addr;
|
||||
}
|
||||
|
||||
void *Heap_RawMalloc(Heap *heap, int size, Error *err)
|
||||
{
|
||||
assert(err);
|
||||
assert(heap);
|
||||
assert(size > -1);
|
||||
|
||||
void *addr;
|
||||
|
||||
int padding = heap->used;
|
||||
|
||||
if(heap->used & 7)
|
||||
heap->used = (heap->used & ~7) + 8;
|
||||
|
||||
padding = heap->used - padding;
|
||||
|
||||
if(heap->used + size > heap->size)
|
||||
{
|
||||
if(heap->collecting)
|
||||
{
|
||||
Error_Report(err, 1, "Out of heap");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
OflowAlloc *oflow = malloc(sizeof(OflowAlloc) + size);
|
||||
|
||||
if(oflow == 0)
|
||||
return 0;
|
||||
|
||||
oflow->prev = heap->oflow;
|
||||
heap->oflow = oflow;
|
||||
|
||||
addr = oflow->body;
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(heap->used + size <= heap->size);
|
||||
addr = heap->body + heap->used;
|
||||
heap->used += size;
|
||||
}
|
||||
|
||||
heap->total += size + padding;
|
||||
|
||||
assert(((intptr_t) addr) % 8 == 0);
|
||||
|
||||
#if USING_VALGRIND
|
||||
VALGRIND_MEMPOOL_ALLOC(heap, addr, size);
|
||||
#endif
|
||||
|
||||
return addr;
|
||||
}
|
||||
|
||||
_Bool Heap_StartCollection(Heap *heap, Error *error)
|
||||
{
|
||||
assert(heap->collecting == 0);
|
||||
|
||||
void *new_body = malloc(heap->size);
|
||||
|
||||
if(new_body == NULL)
|
||||
{
|
||||
Error_Report(error, 1, "No memory");
|
||||
return 0;
|
||||
}
|
||||
|
||||
heap->old_body = heap->body;
|
||||
heap->old_used = heap->used;
|
||||
heap->old_total = heap->total;
|
||||
heap->old_oflow = heap->oflow;
|
||||
heap->total = 0;
|
||||
heap->body = new_body;
|
||||
heap->used = 0;
|
||||
heap->oflow = NULL;
|
||||
heap->collecting = 1;
|
||||
heap->collection_failed = 0;
|
||||
heap->movedcount = 0;
|
||||
heap->error = error;
|
||||
return 1;
|
||||
}
|
||||
|
||||
_Bool Heap_StopCollection(Heap *heap)
|
||||
{
|
||||
assert(heap->collecting == 1);
|
||||
|
||||
if(heap->collection_failed)
|
||||
{
|
||||
free(heap->old_body);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Call destructors here */
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
while(i < heap->pend_used)
|
||||
{
|
||||
Object *obj = heap->pend[i].object;
|
||||
|
||||
if(obj->flags & Object_MOVED)
|
||||
{
|
||||
heap->pend[i].object = ((MovedObject*) heap->pend[i].object)->new_location;
|
||||
i += 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// We need to call the destructor.
|
||||
|
||||
heap->pend[i].destructor(obj, heap->error);
|
||||
|
||||
if(heap->error->occurred)
|
||||
return 0; // There will be leaks.
|
||||
|
||||
heap->pend[i] = heap->pend[heap->pend_used-1];
|
||||
heap->pend_used -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
if(heap->pend_size / 2 > heap->pend_used)
|
||||
{
|
||||
// Downsize
|
||||
void *temp = realloc(heap->pend, heap->pend_size / 2 * sizeof(PendingDestruct));
|
||||
|
||||
if(temp != NULL)
|
||||
{
|
||||
heap->pend = temp;
|
||||
heap->pend_size /= 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while(heap->old_oflow)
|
||||
{
|
||||
OflowAlloc *prev = heap->old_oflow->prev;
|
||||
free(heap->old_oflow);
|
||||
heap->old_oflow = prev;
|
||||
}
|
||||
|
||||
free(heap->old_body);
|
||||
|
||||
heap->collecting = 0;
|
||||
heap->objcount = heap->movedcount;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void Heap_CollectExtension(void **referer, unsigned int size, void *userp)
|
||||
{
|
||||
Heap *heap = userp;
|
||||
|
||||
assert(referer != NULL);
|
||||
assert(heap->collecting);
|
||||
|
||||
void *old_location = *referer;
|
||||
|
||||
if(heap->collection_failed || old_location == NULL)
|
||||
return;
|
||||
|
||||
void *new_location = Heap_RawMalloc(heap, size, heap->error);
|
||||
|
||||
if(new_location == NULL)
|
||||
{
|
||||
heap->collection_failed = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(new_location, old_location, size);
|
||||
|
||||
*referer = new_location;
|
||||
}
|
||||
|
||||
void Heap_CollectReference(Object **referer, void *userp)
|
||||
{
|
||||
Heap *heap = userp;
|
||||
|
||||
assert(referer != NULL);
|
||||
assert(heap->collecting);
|
||||
|
||||
Object *old_location = *referer;
|
||||
|
||||
if(heap->collection_failed || old_location == NULL)
|
||||
return;
|
||||
|
||||
if(old_location->flags & Object_MOVED)
|
||||
|
||||
// The object was already moved.
|
||||
*referer = ((MovedObject*) old_location)->new_location;
|
||||
|
||||
else
|
||||
{
|
||||
Object *new_location;
|
||||
|
||||
// This object wasn't moved to
|
||||
// the new heap yet.
|
||||
|
||||
if(old_location->flags & Object_STATIC)
|
||||
|
||||
// The object doesn't need to be moved
|
||||
// since it was statically allocated.
|
||||
new_location = old_location;
|
||||
else
|
||||
{
|
||||
// Get some information.
|
||||
TypeObject *type = old_location->type;
|
||||
int size = type->size;
|
||||
|
||||
// Copy the object to a new location.
|
||||
{
|
||||
new_location = Heap_RawMalloc(heap, size, heap->error);
|
||||
|
||||
if(new_location == NULL)
|
||||
{
|
||||
heap->collection_failed = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(new_location, old_location, size);
|
||||
}
|
||||
|
||||
// Set the old location as moved and
|
||||
// leave the reference to the new
|
||||
// location.
|
||||
{
|
||||
old_location->flags |= Object_MOVED;
|
||||
|
||||
assert((int) sizeof(MovedObject) <= size);
|
||||
((MovedObject*) old_location)->new_location = new_location;
|
||||
}
|
||||
|
||||
heap->movedcount += 1;
|
||||
}
|
||||
|
||||
// Collect the reference to the type.
|
||||
if((Object*) new_location->type != new_location)
|
||||
Heap_CollectReference((Object**) &new_location->type, heap);
|
||||
|
||||
// Collect all of the references to
|
||||
// extensions allocate using the GC'd
|
||||
// heap.
|
||||
Object_WalkExtensions(new_location,
|
||||
Heap_CollectExtension, heap);
|
||||
|
||||
// Now collect all of the children.
|
||||
Object_WalkReferences(new_location,
|
||||
Heap_CollectReference, heap);
|
||||
|
||||
// Update the referer
|
||||
*referer = new_location;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
|
||||
/* +--------------------------------------------------------------------------+
|
||||
** | _ _ _ |
|
||||
** | | \ | | (_) |
|
||||
** | | \| | ___ _ __ _ |
|
||||
** | | . ` |/ _ \| |/ _` | |
|
||||
** | | |\ | (_) | | (_| | |
|
||||
** | |_| \_|\___/| |\__,_| |
|
||||
** | _/ | |
|
||||
** | |__/ |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | Copyright (c) 2022 Francesco Cozzuto <francesco.cozzuto@gmail.com> |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | This file is part of The Noja Interpreter. |
|
||||
** | |
|
||||
** | The Noja Interpreter is free software: you can redistribute it and/or |
|
||||
** | modify it under the terms of the GNU General Public License as published |
|
||||
** | by the Free Software Foundation, either version 3 of the License, or (at |
|
||||
** | your option) any later version. |
|
||||
** | |
|
||||
** | The Noja Interpreter is distributed in the hope that it will be useful, |
|
||||
** | but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||||
** | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General |
|
||||
** | Public License for more details. |
|
||||
** | |
|
||||
** | You should have received a copy of the GNU General Public License along |
|
||||
** | with The Noja Interpreter. If not, see <http://www.gnu.org/licenses/>. |
|
||||
** +--------------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include "objects.h"
|
||||
|
||||
static _Bool to_bool(Object *obj, Error *err);
|
||||
static void print(Object *obj, FILE *fp);
|
||||
static _Bool op_eql(Object *self, Object *other);
|
||||
static int hash(Object *self);
|
||||
static Object *copy(Object *self, Heap *heap, Error *err);
|
||||
|
||||
static TypeObject t_bool = {
|
||||
.base = (Object) { .type = &t_type, .flags = Object_STATIC },
|
||||
.name = "bool",
|
||||
.size = sizeof (Object),
|
||||
.atomic = ATMTP_BOOL,
|
||||
.hash = hash,
|
||||
.copy = copy,
|
||||
.to_bool = to_bool,
|
||||
.print = print,
|
||||
.op_eql = op_eql,
|
||||
};
|
||||
|
||||
static Object the_true_object = {
|
||||
.type = &t_bool,
|
||||
.flags = Object_STATIC,
|
||||
};
|
||||
|
||||
static Object the_false_object = {
|
||||
.type = &t_bool,
|
||||
.flags = Object_STATIC,
|
||||
};
|
||||
|
||||
static int hash(Object *self)
|
||||
{
|
||||
assert(self != NULL);
|
||||
assert(self->type == &t_bool);
|
||||
|
||||
if(self == &the_true_object)
|
||||
return 1;
|
||||
|
||||
assert(self == &the_false_object);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static Object *copy(Object *self, Heap *heap, Error *err)
|
||||
{
|
||||
(void) heap;
|
||||
(void) err;
|
||||
return self;
|
||||
}
|
||||
|
||||
static _Bool op_eql(Object *self, Object *other)
|
||||
{
|
||||
assert(self != NULL);
|
||||
assert(self->type == &t_bool);
|
||||
assert(other != NULL);
|
||||
assert(other->type == &t_bool);
|
||||
|
||||
return self == other;
|
||||
}
|
||||
|
||||
static _Bool to_bool(Object *obj, Error *err)
|
||||
{
|
||||
assert(obj);
|
||||
assert(err);
|
||||
assert(Object_GetType(obj) == &t_bool);
|
||||
return obj == &the_true_object;
|
||||
}
|
||||
|
||||
Object *Object_FromBool(_Bool val, Heap *heap, Error *error)
|
||||
{
|
||||
(void) heap;
|
||||
(void) error;
|
||||
return val ? &the_true_object : &the_false_object;
|
||||
}
|
||||
|
||||
static void print(Object *obj, FILE *fp)
|
||||
{
|
||||
assert(fp != NULL);
|
||||
assert(obj != NULL);
|
||||
assert(obj->type == &t_bool);
|
||||
|
||||
fprintf(fp, obj == &the_true_object ? "true" : "false");
|
||||
}
|
||||
@@ -0,0 +1,450 @@
|
||||
|
||||
/* +--------------------------------------------------------------------------+
|
||||
** | _ _ _ |
|
||||
** | | \ | | (_) |
|
||||
** | | \| | ___ _ __ _ |
|
||||
** | | . ` |/ _ \| |/ _` | |
|
||||
** | | |\ | (_) | | (_| | |
|
||||
** | |_| \_|\___/| |\__,_| |
|
||||
** | _/ | |
|
||||
** | |__/ |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | Copyright (c) 2022 Francesco Cozzuto <francesco.cozzuto@gmail.com> |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | This file is part of The Noja Interpreter. |
|
||||
** | |
|
||||
** | The Noja Interpreter is free software: you can redistribute it and/or |
|
||||
** | modify it under the terms of the GNU General Public License as published |
|
||||
** | by the Free Software Foundation, either version 3 of the License, or (at |
|
||||
** | your option) any later version. |
|
||||
** | |
|
||||
** | The Noja Interpreter is distributed in the hope that it will be useful, |
|
||||
** | but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||||
** | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General |
|
||||
** | Public License for more details. |
|
||||
** | |
|
||||
** | You should have received a copy of the GNU General Public License along |
|
||||
** | with The Noja Interpreter. If not, see <http://www.gnu.org/licenses/>. |
|
||||
** +--------------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include "../utils/defs.h"
|
||||
#include "objects.h"
|
||||
|
||||
typedef struct {
|
||||
Object base;
|
||||
int size;
|
||||
unsigned char *body;
|
||||
} BufferObject;
|
||||
|
||||
typedef struct {
|
||||
Object base;
|
||||
BufferObject *sliced;
|
||||
int offset,
|
||||
length;
|
||||
} BufferSliceObject;
|
||||
|
||||
static Object *buffer_select(Object *self, Object *key, Heap *heap, Error *err);
|
||||
static _Bool buffer_insert(Object *self, Object *key, Object *val, Heap *heap, Error *err);
|
||||
static int buffer_count(Object *self);
|
||||
static void buffer_print(Object *obj, FILE *fp);
|
||||
static _Bool buffer_free(Object *self, Error *error);
|
||||
|
||||
static Object *slice_select(Object *self, Object *key, Heap *heap, Error *err);
|
||||
static _Bool slice_insert(Object *self, Object *key, Object *val, Heap *heap, Error *err);
|
||||
static int slice_count(Object *self);
|
||||
static void slice_print(Object *obj, FILE *fp);
|
||||
|
||||
|
||||
static TypeObject t_buffer = {
|
||||
.base = (Object) { .type = &t_type, .flags = Object_STATIC },
|
||||
.name = "buffer",
|
||||
.size = sizeof (BufferObject),
|
||||
.select = buffer_select,
|
||||
.insert = buffer_insert,
|
||||
.count = buffer_count,
|
||||
.print = buffer_print,
|
||||
.free = buffer_free,
|
||||
};
|
||||
|
||||
static TypeObject t_buffer_slice = {
|
||||
.base = (Object) { .type = &t_type, .flags = Object_STATIC },
|
||||
.name = "buffer slice",
|
||||
.size = sizeof (BufferSliceObject),
|
||||
.select = slice_select,
|
||||
.insert = slice_insert,
|
||||
.count = slice_count,
|
||||
.print = slice_print,
|
||||
};
|
||||
|
||||
#define THRESHOLD 128
|
||||
|
||||
_Bool Object_IsBuffer(Object *obj)
|
||||
{
|
||||
return obj->type == &t_buffer_slice || obj->type == &t_buffer;
|
||||
}
|
||||
|
||||
Object *Object_NewBuffer(int size, Heap *heap, Error *error)
|
||||
{
|
||||
assert(size >= 0);
|
||||
|
||||
// Make the thing.
|
||||
BufferObject *obj;
|
||||
{
|
||||
obj = (BufferObject*) Heap_Malloc(heap, &t_buffer, error);
|
||||
|
||||
if(obj == NULL)
|
||||
return NULL;
|
||||
|
||||
unsigned char *body;
|
||||
|
||||
if(size > THRESHOLD)
|
||||
{
|
||||
body = malloc(sizeof(unsigned char) * size);
|
||||
|
||||
if(body == NULL)
|
||||
{
|
||||
Error_Report(error, 1, "No memory");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
body = Heap_RawMalloc(heap, sizeof(unsigned char) * size, error);
|
||||
|
||||
if(body == NULL)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
obj->size = size;
|
||||
obj->body = body;
|
||||
memset(obj->body, 0, size);
|
||||
}
|
||||
|
||||
return (Object*) obj;
|
||||
}
|
||||
|
||||
static _Bool buffer_free(Object *self, Error *error)
|
||||
{
|
||||
(void) error;
|
||||
|
||||
BufferObject *buffer = (BufferObject*) self;
|
||||
|
||||
if(buffer->size > THRESHOLD)
|
||||
free(buffer->body);
|
||||
return 1;
|
||||
}
|
||||
|
||||
Object *Object_SliceBuffer(Object *buffer, int offset, int length, Heap *heap, Error *error)
|
||||
{
|
||||
if(buffer->type != &t_buffer && buffer->type != &t_buffer_slice)
|
||||
{
|
||||
Error_Report(error, 0, "Not a buffer or a buffer slice");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
BufferSliceObject *slice;
|
||||
|
||||
if(buffer->type == &t_buffer)
|
||||
{
|
||||
BufferObject *original = (BufferObject*) buffer;
|
||||
|
||||
if(offset == 0 && length == original->size)
|
||||
return buffer;
|
||||
|
||||
slice = (BufferSliceObject*) Heap_Malloc(heap, &t_buffer_slice, error);
|
||||
|
||||
if(slice == NULL)
|
||||
return NULL;
|
||||
|
||||
if(offset < 0 || offset >= original->size)
|
||||
{
|
||||
Error_Report(error, 0, "offset out of range");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if(length < 0 || length >= original->size)
|
||||
{
|
||||
Error_Report(error, 0, "length out of range");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if(offset + length > original->size)
|
||||
{
|
||||
Error_Report(error, 0, "slice out of range");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
slice->sliced = original;
|
||||
slice->offset = offset;
|
||||
slice->length = length;
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(buffer->type == &t_buffer_slice);
|
||||
|
||||
slice = (BufferSliceObject*) Heap_Malloc(heap, &t_buffer_slice, error);
|
||||
|
||||
if(slice == NULL)
|
||||
return NULL;
|
||||
|
||||
BufferSliceObject *original = (BufferSliceObject*) buffer;
|
||||
|
||||
if(offset < 0 || offset >= original->length)
|
||||
{
|
||||
Error_Report(error, 0, "offset out of range");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if(length < 0 || length >= original->length)
|
||||
{
|
||||
Error_Report(error, 0, "length out of range");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if(offset + length > original->length)
|
||||
{
|
||||
Error_Report(error, 0, "slice out of range");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
slice->sliced = original->sliced;
|
||||
slice->offset = original->offset + offset;
|
||||
slice->length = length;
|
||||
}
|
||||
|
||||
return (Object*) slice;
|
||||
}
|
||||
|
||||
void *Object_GetBufferAddrAndSize(Object *obj, int *size, Error *error)
|
||||
{
|
||||
if(obj->type != &t_buffer && obj->type != &t_buffer_slice)
|
||||
{
|
||||
Error_Report(error, 0, "Not a buffer or a buffer slice");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if(obj->type == &t_buffer)
|
||||
{
|
||||
BufferObject *buffer = (BufferObject*) obj;
|
||||
|
||||
if(size)
|
||||
*size = buffer->size;
|
||||
|
||||
return buffer->body;
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(obj->type == &t_buffer_slice);
|
||||
|
||||
BufferSliceObject *slice = (BufferSliceObject*) obj;
|
||||
|
||||
if(size)
|
||||
*size = slice->length;
|
||||
|
||||
return slice->sliced->body + slice->offset;
|
||||
}
|
||||
}
|
||||
|
||||
static Object *buffer_select(Object *self, Object *key, Heap *heap, Error *error)
|
||||
{
|
||||
assert(self != NULL);
|
||||
assert(self->type == &t_buffer);
|
||||
assert(key != NULL);
|
||||
assert(heap != NULL);
|
||||
assert(error != NULL);
|
||||
|
||||
if(!Object_IsInt(key))
|
||||
{
|
||||
Error_Report(error, 0, "Non integer key");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int idx = Object_ToInt(key, error);
|
||||
assert(error->occurred == 0);
|
||||
|
||||
BufferObject *buffer = (BufferObject*) self;
|
||||
|
||||
if(idx < 0 || idx >= buffer->size)
|
||||
{
|
||||
Error_Report(error, 0, "Out of range index");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
unsigned char byte = buffer->body[idx];
|
||||
|
||||
return Object_FromInt(byte, heap, error);
|
||||
}
|
||||
|
||||
static Object *slice_select(Object *self, Object *key, Heap *heap, Error *error)
|
||||
{
|
||||
assert(self != NULL);
|
||||
assert(self->type == &t_buffer_slice);
|
||||
assert(key != NULL);
|
||||
assert(heap != NULL);
|
||||
assert(error != NULL);
|
||||
|
||||
if(!Object_IsInt(key))
|
||||
{
|
||||
Error_Report(error, 0, "Non integer key");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int idx = Object_ToInt(key, error);
|
||||
assert(error->occurred == 0);
|
||||
|
||||
BufferSliceObject *slice = (BufferSliceObject*) self;
|
||||
|
||||
if(idx < 0 || idx >= slice->length)
|
||||
{
|
||||
Error_Report(error, 0, "Out of range index");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
unsigned char byte = slice->sliced->body[slice->offset + idx];
|
||||
|
||||
return Object_FromInt(byte, heap, error);
|
||||
}
|
||||
|
||||
static _Bool buffer_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_buffer);
|
||||
|
||||
BufferObject *buffer = (BufferObject*) self;
|
||||
|
||||
if(!Object_IsInt(key))
|
||||
{
|
||||
Error_Report(error, 0, "Non integer key");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int idx = Object_ToInt(key, error);
|
||||
assert(error->occurred == 0);
|
||||
|
||||
if(idx < 0 || idx >= buffer->size)
|
||||
{
|
||||
Error_Report(error, 0, "Out of range index");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
long long int qword = Object_ToInt(val, error);
|
||||
|
||||
if(qword > 255 || qword < 0)
|
||||
{
|
||||
Error_Report(error, 0, "Not in range [0, 255]");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
unsigned char byte = qword & 0xff;
|
||||
|
||||
if(error->occurred == 1)
|
||||
return 0;
|
||||
|
||||
buffer->body[idx] = byte;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static _Bool slice_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_buffer_slice);
|
||||
|
||||
BufferSliceObject *slice = (BufferSliceObject*) self;
|
||||
|
||||
if(!Object_IsInt(key))
|
||||
{
|
||||
Error_Report(error, 0, "Non integer key");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int idx = Object_ToInt(key, error);
|
||||
assert(error->occurred == 0);
|
||||
|
||||
if(idx < 0 || idx >= slice->length)
|
||||
{
|
||||
Error_Report(error, 0, "Out of range index");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
long long int qword = Object_ToInt(val, error);
|
||||
|
||||
if(qword > 255 || qword < 0)
|
||||
{
|
||||
Error_Report(error, 0, "Not in range [0, 255]");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
unsigned char byte = qword & 0xff;
|
||||
|
||||
if(error->occurred == 1)
|
||||
return 0;
|
||||
|
||||
slice->sliced->body[slice->offset + idx] = byte;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int buffer_count(Object *self)
|
||||
{
|
||||
BufferObject *buffer = (BufferObject*) self;
|
||||
|
||||
return buffer->size;
|
||||
}
|
||||
|
||||
static int slice_count(Object *self)
|
||||
{
|
||||
BufferSliceObject *slice = (BufferSliceObject*) self;
|
||||
|
||||
return slice->length;
|
||||
}
|
||||
|
||||
static void print_bytes(FILE *fp, unsigned char *addr, int size)
|
||||
{
|
||||
fprintf(fp, "[");
|
||||
|
||||
for(int i = 0; i < size; i += 1)
|
||||
{
|
||||
unsigned char byte, low, high;
|
||||
|
||||
byte = addr[i];
|
||||
low = byte & 0xf;
|
||||
high = byte >> 4;
|
||||
|
||||
assert(low < 16);
|
||||
assert(high < 16);
|
||||
|
||||
char c1, c2;
|
||||
|
||||
c1 = low < 10 ? low + '0' : low - 10 + 'A';
|
||||
c2 = high < 10 ? high + '0' : high - 10 + 'A';
|
||||
|
||||
fprintf(fp, "%c%c", c2, c1);
|
||||
|
||||
if(i+1 < size)
|
||||
fprintf(fp, ", ");
|
||||
}
|
||||
fprintf(fp, "]");
|
||||
}
|
||||
|
||||
static void buffer_print(Object *self, FILE *fp)
|
||||
{
|
||||
BufferObject *buffer = (BufferObject*) self;
|
||||
print_bytes(fp, buffer->body, buffer->size);
|
||||
}
|
||||
|
||||
static void slice_print(Object *self, FILE *fp)
|
||||
{
|
||||
BufferSliceObject *slice = (BufferSliceObject*) self;
|
||||
print_bytes(fp, slice->sliced->body + slice->offset, slice->length);
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
|
||||
/* +--------------------------------------------------------------------------+
|
||||
** | _ _ _ |
|
||||
** | | \ | | (_) |
|
||||
** | | \| | ___ _ __ _ |
|
||||
** | | . ` |/ _ \| |/ _` | |
|
||||
** | | |\ | (_) | | (_| | |
|
||||
** | |_| \_|\___/| |\__,_| |
|
||||
** | _/ | |
|
||||
** | |__/ |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | Copyright (c) 2022 Francesco Cozzuto <francesco.cozzuto@gmail.com> |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | This file is part of The Noja Interpreter. |
|
||||
** | |
|
||||
** | The Noja Interpreter is free software: you can redistribute it and/or |
|
||||
** | modify it under the terms of the GNU General Public License as published |
|
||||
** | by the Free Software Foundation, either version 3 of the License, or (at |
|
||||
** | your option) any later version. |
|
||||
** | |
|
||||
** | The Noja Interpreter is distributed in the hope that it will be useful, |
|
||||
** | but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||||
** | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General |
|
||||
** | Public License for more details. |
|
||||
** | |
|
||||
** | You should have received a copy of the GNU General Public License along |
|
||||
** | with The Noja Interpreter. If not, see <http://www.gnu.org/licenses/>. |
|
||||
** +--------------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#include "../utils/defs.h"
|
||||
#include "objects.h"
|
||||
|
||||
typedef struct ClosureObject ClosureObject;
|
||||
|
||||
struct ClosureObject {
|
||||
Object base;
|
||||
ClosureObject *prev;
|
||||
Object *vars;
|
||||
};
|
||||
|
||||
static void walk(Object *self, void (*callback)(Object **referer, void *userp), void *userp);
|
||||
static Object *select(Object *self, Object *key, Heap *heap, Error *err);
|
||||
|
||||
static TypeObject t_closure = {
|
||||
.base = (Object) { .type = &t_type, .flags = Object_STATIC },
|
||||
.name = "closure",
|
||||
.size = sizeof(ClosureObject),
|
||||
.select = select,
|
||||
.walk = walk,
|
||||
};
|
||||
|
||||
Object *Object_NewClosure(Object *parent, Object *new_map, Heap *heap, Error *error)
|
||||
{
|
||||
ClosureObject *obj = (ClosureObject*) Heap_Malloc(heap, &t_closure, error);
|
||||
|
||||
if(obj == NULL)
|
||||
return NULL;
|
||||
|
||||
if(parent != NULL && parent->type != &t_closure)
|
||||
{
|
||||
Error_Report(error, 0, "Object is not a closure");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
obj->prev = (ClosureObject*) parent;
|
||||
obj->vars = new_map;
|
||||
|
||||
return (Object*) obj;
|
||||
}
|
||||
|
||||
static Object *select(Object *self, Object *key, Heap *heap, Error *err)
|
||||
{
|
||||
ClosureObject *closure = (ClosureObject*) self;
|
||||
|
||||
Object *selected = NULL;
|
||||
|
||||
while(closure != NULL && selected == NULL)
|
||||
{
|
||||
selected = Object_Select(closure->vars, key, heap, err);
|
||||
|
||||
if(err->occurred)
|
||||
return NULL;
|
||||
|
||||
closure = closure->prev;
|
||||
}
|
||||
|
||||
return selected;
|
||||
}
|
||||
|
||||
static void walk(Object *self, void (*callback)(Object **referer, void *userp), void *userp)
|
||||
{
|
||||
ClosureObject *closure = (ClosureObject*) self;
|
||||
|
||||
callback((Object**) &closure->prev, userp);
|
||||
callback(&closure->vars, userp);
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
|
||||
/* +--------------------------------------------------------------------------+
|
||||
** | _ _ _ |
|
||||
** | | \ | | (_) |
|
||||
** | | \| | ___ _ __ _ |
|
||||
** | | . ` |/ _ \| |/ _` | |
|
||||
** | | |\ | (_) | | (_| | |
|
||||
** | |_| \_|\___/| |\__,_| |
|
||||
** | _/ | |
|
||||
** | |__/ |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | Copyright (c) 2022 Francesco Cozzuto <francesco.cozzuto@gmail.com> |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | This file is part of The Noja Interpreter. |
|
||||
** | |
|
||||
** | The Noja Interpreter is free software: you can redistribute it and/or |
|
||||
** | modify it under the terms of the GNU General Public License as published |
|
||||
** | by the Free Software Foundation, either version 3 of the License, or (at |
|
||||
** | your option) any later version. |
|
||||
** | |
|
||||
** | The Noja Interpreter is distributed in the hope that it will be useful, |
|
||||
** | but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||||
** | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General |
|
||||
** | Public License for more details. |
|
||||
** | |
|
||||
** | You should have received a copy of the GNU General Public License along |
|
||||
** | with The Noja Interpreter. If not, see <http://www.gnu.org/licenses/>. |
|
||||
** +--------------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#include "objects.h"
|
||||
|
||||
typedef struct {
|
||||
Object base;
|
||||
DIR *dir;
|
||||
} DirObject;
|
||||
|
||||
static _Bool dir_free(Object *obj, Error *error);
|
||||
|
||||
static TypeObject t_dir = {
|
||||
.base = (Object) { .type = &t_type, .flags = Object_STATIC },
|
||||
.name = "Directory",
|
||||
.size = sizeof(DirObject),
|
||||
.free = dir_free,
|
||||
};
|
||||
|
||||
_Bool Object_IsDir(Object *obj)
|
||||
{
|
||||
return obj->type == &t_dir;
|
||||
}
|
||||
|
||||
Object *Object_FromDIR(DIR *handle, Heap *heap, Error *error)
|
||||
{
|
||||
DirObject *dob = (DirObject*) Heap_Malloc(heap, &t_dir, error);
|
||||
|
||||
if(dob == NULL)
|
||||
return NULL;
|
||||
|
||||
dob->dir = handle;
|
||||
|
||||
return (Object*) dob;
|
||||
}
|
||||
|
||||
DIR *Object_ToDIR(Object *obj, Error *error)
|
||||
{
|
||||
if(!Object_IsDir(obj))
|
||||
{
|
||||
Error_Report(error, 0, "Object is not a directory");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return ((DirObject*) obj)->dir;
|
||||
}
|
||||
|
||||
static _Bool dir_free(Object *obj, Error *error)
|
||||
{
|
||||
DirObject *dob = (DirObject*) obj;
|
||||
if(closedir(dob->dir) == 0)
|
||||
return 1;
|
||||
|
||||
Error_Report(error, 0, "Failed to close directory");
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
|
||||
/* +--------------------------------------------------------------------------+
|
||||
** | _ _ _ |
|
||||
** | | \ | | (_) |
|
||||
** | | \| | ___ _ __ _ |
|
||||
** | | . ` |/ _ \| |/ _` | |
|
||||
** | | |\ | (_) | | (_| | |
|
||||
** | |_| \_|\___/| |\__,_| |
|
||||
** | _/ | |
|
||||
** | |__/ |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | Copyright (c) 2022 Francesco Cozzuto <francesco.cozzuto@gmail.com> |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | This file is part of The Noja Interpreter. |
|
||||
** | |
|
||||
** | The Noja Interpreter is free software: you can redistribute it and/or |
|
||||
** | modify it under the terms of the GNU General Public License as published |
|
||||
** | by the Free Software Foundation, either version 3 of the License, or (at |
|
||||
** | your option) any later version. |
|
||||
** | |
|
||||
** | The Noja Interpreter is distributed in the hope that it will be useful, |
|
||||
** | but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||||
** | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General |
|
||||
** | Public License for more details. |
|
||||
** | |
|
||||
** | You should have received a copy of the GNU General Public License along |
|
||||
** | with The Noja Interpreter. If not, see <http://www.gnu.org/licenses/>. |
|
||||
** +--------------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#include "objects.h"
|
||||
|
||||
typedef struct {
|
||||
Object base;
|
||||
FILE *fp;
|
||||
} FileObject;
|
||||
|
||||
static _Bool file_free(Object *self, Error *error);
|
||||
|
||||
static TypeObject t_file = {
|
||||
.base = (Object) { .type = &t_type, .flags = Object_STATIC },
|
||||
.name = "File",
|
||||
.size = sizeof(FileObject),
|
||||
.free = file_free,
|
||||
};
|
||||
|
||||
_Bool Object_IsFile(Object *obj)
|
||||
{
|
||||
return obj->type == &t_file;
|
||||
}
|
||||
|
||||
FILE *Object_ToStream(Object *obj, Error *error)
|
||||
{
|
||||
if(!Object_IsFile(obj))
|
||||
{
|
||||
Error_Report(error, 0, "Object is not a file");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return ((FileObject*) obj)->fp;
|
||||
}
|
||||
|
||||
Object *Object_FromStream(FILE *fp, Heap *heap, Error *error)
|
||||
{
|
||||
FileObject *fob = Heap_Malloc(heap, &t_file, error);
|
||||
|
||||
if(fob == NULL)
|
||||
return NULL;
|
||||
|
||||
fob->fp = fp;
|
||||
|
||||
return (Object*) fob;
|
||||
}
|
||||
|
||||
static _Bool file_free(Object *self, Error *error)
|
||||
{
|
||||
FileObject *fob = (FileObject*) self;
|
||||
if(fclose(fob->fp) == 0)
|
||||
return 1;
|
||||
|
||||
Error_Report(error, 0, "Failed to close stream");
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
|
||||
/* +--------------------------------------------------------------------------+
|
||||
** | _ _ _ |
|
||||
** | | \ | | (_) |
|
||||
** | | \| | ___ _ __ _ |
|
||||
** | | . ` |/ _ \| |/ _` | |
|
||||
** | | |\ | (_) | | (_| | |
|
||||
** | |_| \_|\___/| |\__,_| |
|
||||
** | _/ | |
|
||||
** | |__/ |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | Copyright (c) 2022 Francesco Cozzuto <francesco.cozzuto@gmail.com> |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | This file is part of The Noja Interpreter. |
|
||||
** | |
|
||||
** | The Noja Interpreter is free software: you can redistribute it and/or |
|
||||
** | modify it under the terms of the GNU General Public License as published |
|
||||
** | by the Free Software Foundation, either version 3 of the License, or (at |
|
||||
** | your option) any later version. |
|
||||
** | |
|
||||
** | The Noja Interpreter is distributed in the hope that it will be useful, |
|
||||
** | but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||||
** | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General |
|
||||
** | Public License for more details. |
|
||||
** | |
|
||||
** | You should have received a copy of the GNU General Public License along |
|
||||
** | with The Noja Interpreter. If not, see <http://www.gnu.org/licenses/>. |
|
||||
** +--------------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include "objects.h"
|
||||
#include "../utils/hash.h"
|
||||
|
||||
static double to_float(Object *obj, Error *err);
|
||||
static void print(Object *obj, FILE *fp);
|
||||
static _Bool op_eql(Object *self, Object *other);
|
||||
static int hash(Object *self);
|
||||
static Object *copy(Object *self, Heap *heap, Error *err);
|
||||
|
||||
typedef struct {
|
||||
Object base;
|
||||
double val;
|
||||
} FloatObject;
|
||||
|
||||
static TypeObject t_float = {
|
||||
.base = (Object) { .type = &t_type, .flags = Object_STATIC },
|
||||
.name = "float",
|
||||
.size = sizeof (FloatObject),
|
||||
.atomic = ATMTP_FLOAT,
|
||||
.hash = hash,
|
||||
.copy = copy,
|
||||
.to_float = to_float,
|
||||
.print = print,
|
||||
.op_eql = op_eql
|
||||
};
|
||||
|
||||
static int hash(Object *self)
|
||||
{
|
||||
assert(self != NULL);
|
||||
assert(self->type == &t_float);
|
||||
|
||||
FloatObject *iobj = (FloatObject*) self;
|
||||
|
||||
return hashbytes((unsigned char*) &iobj->val, sizeof(iobj->val));
|
||||
}
|
||||
|
||||
static Object *copy(Object *self, Heap *heap, Error *err)
|
||||
{
|
||||
(void) heap;
|
||||
(void) err;
|
||||
return self;
|
||||
}
|
||||
|
||||
static _Bool op_eql(Object *self, Object *other)
|
||||
{
|
||||
assert(self != NULL);
|
||||
assert(self->type == &t_float);
|
||||
assert(other != NULL);
|
||||
assert(other->type == &t_float);
|
||||
|
||||
FloatObject *i1, *i2;
|
||||
|
||||
i1 = (FloatObject*) self;
|
||||
i2 = (FloatObject*) other;
|
||||
|
||||
return i1->val == i2->val;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static void print(Object *obj, FILE *fp)
|
||||
{
|
||||
assert(fp != NULL);
|
||||
assert(obj != NULL);
|
||||
assert(obj->type == &t_float);
|
||||
|
||||
fprintf(fp, "%2.2f", ((FloatObject*) obj)->val);
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
|
||||
/* +--------------------------------------------------------------------------+
|
||||
** | _ _ _ |
|
||||
** | | \ | | (_) |
|
||||
** | | \| | ___ _ __ _ |
|
||||
** | | . ` |/ _ \| |/ _` | |
|
||||
** | | |\ | (_) | | (_| | |
|
||||
** | |_| \_|\___/| |\__,_| |
|
||||
** | _/ | |
|
||||
** | |__/ |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | Copyright (c) 2022 Francesco Cozzuto <francesco.cozzuto@gmail.com> |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | This file is part of The Noja Interpreter. |
|
||||
** | |
|
||||
** | The Noja Interpreter is free software: you can redistribute it and/or |
|
||||
** | modify it under the terms of the GNU General Public License as published |
|
||||
** | by the Free Software Foundation, either version 3 of the License, or (at |
|
||||
** | your option) any later version. |
|
||||
** | |
|
||||
** | The Noja Interpreter is distributed in the hope that it will be useful, |
|
||||
** | but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||||
** | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General |
|
||||
** | Public License for more details. |
|
||||
** | |
|
||||
** | You should have received a copy of the GNU General Public License along |
|
||||
** | with The Noja Interpreter. If not, see <http://www.gnu.org/licenses/>. |
|
||||
** +--------------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include "objects.h"
|
||||
#include "../utils/hash.h"
|
||||
|
||||
static long long int to_int(Object *obj, Error *err);
|
||||
static void print(Object *obj, FILE *fp);
|
||||
static _Bool op_eql(Object *self, Object *other);
|
||||
static int hash(Object *self);
|
||||
static Object *copy(Object *self, Heap *heap, Error *err);
|
||||
|
||||
typedef struct {
|
||||
Object base;
|
||||
long long int val;
|
||||
} IntObject;
|
||||
|
||||
static TypeObject t_int = {
|
||||
.base = (Object) { .type = &t_type, .flags = Object_STATIC },
|
||||
.name = "int",
|
||||
.size = sizeof (IntObject),
|
||||
.atomic = ATMTP_INT,
|
||||
.hash = hash,
|
||||
.copy = copy,
|
||||
.to_int = to_int,
|
||||
.print = print,
|
||||
.op_eql = op_eql,
|
||||
};
|
||||
|
||||
static int hash(Object *self)
|
||||
{
|
||||
assert(self != NULL);
|
||||
assert(self->type == &t_int);
|
||||
|
||||
IntObject *iobj = (IntObject*) self;
|
||||
|
||||
return hashbytes((unsigned char*) &iobj->val, sizeof(iobj->val));
|
||||
}
|
||||
|
||||
static Object *copy(Object *self, Heap *heap, Error *err)
|
||||
{
|
||||
(void) heap;
|
||||
(void) err;
|
||||
return self;
|
||||
}
|
||||
|
||||
static long long int to_int(Object *obj, Error *err)
|
||||
{
|
||||
assert(obj != NULL);
|
||||
assert(err != NULL);
|
||||
assert(Object_GetType(obj) == &t_int);
|
||||
|
||||
(void) err;
|
||||
|
||||
return ((IntObject*) obj)->val;
|
||||
}
|
||||
|
||||
Object *Object_FromInt(long long int val, Heap *heap, Error *error)
|
||||
{
|
||||
assert(heap != NULL);
|
||||
assert(error != NULL);
|
||||
|
||||
IntObject *obj = (IntObject*) Heap_Malloc(heap, &t_int, error);
|
||||
|
||||
if(obj == 0)
|
||||
return 0;
|
||||
|
||||
obj->val = val;
|
||||
|
||||
return (Object*) obj;
|
||||
}
|
||||
|
||||
static void print(Object *obj, FILE *fp)
|
||||
{
|
||||
assert(fp != NULL);
|
||||
assert(obj != NULL);
|
||||
assert(obj->type == &t_int);
|
||||
|
||||
fprintf(fp, "%lld", ((IntObject*) obj)->val);
|
||||
}
|
||||
|
||||
static _Bool op_eql(Object *self, Object *other)
|
||||
{
|
||||
assert(self != NULL);
|
||||
assert(self->type == &t_int);
|
||||
assert(other != NULL);
|
||||
assert(other->type == &t_int);
|
||||
|
||||
IntObject *i1, *i2;
|
||||
|
||||
i1 = (IntObject*) self;
|
||||
i2 = (IntObject*) other;
|
||||
|
||||
return i1->val == i2->val;
|
||||
}
|
||||
@@ -0,0 +1,272 @@
|
||||
|
||||
/* +--------------------------------------------------------------------------+
|
||||
** | _ _ _ |
|
||||
** | | \ | | (_) |
|
||||
** | | \| | ___ _ __ _ |
|
||||
** | | . ` |/ _ \| |/ _` | |
|
||||
** | | |\ | (_) | | (_| | |
|
||||
** | |_| \_|\___/| |\__,_| |
|
||||
** | _/ | |
|
||||
** | |__/ |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | Copyright (c) 2022 Francesco Cozzuto <francesco.cozzuto@gmail.com> |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | This file is part of The Noja Interpreter. |
|
||||
** | |
|
||||
** | The Noja Interpreter is free software: you can redistribute it and/or |
|
||||
** | modify it under the terms of the GNU General Public License as published |
|
||||
** | by the Free Software Foundation, either version 3 of the License, or (at |
|
||||
** | your option) any later version. |
|
||||
** | |
|
||||
** | The Noja Interpreter is distributed in the hope that it will be useful, |
|
||||
** | but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||||
** | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General |
|
||||
** | Public License for more details. |
|
||||
** | |
|
||||
** | You should have received a copy of the GNU General Public License along |
|
||||
** | with The Noja Interpreter. If not, see <http://www.gnu.org/licenses/>. |
|
||||
** +--------------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include "../utils/defs.h"
|
||||
#include "objects.h"
|
||||
|
||||
typedef struct {
|
||||
Object base;
|
||||
int capacity, count;
|
||||
Object **vals;
|
||||
} ListObject;
|
||||
|
||||
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 void print(Object *obj, FILE *fp);
|
||||
static void walk(Object *self, void (*callback)(Object **referer, void *userp), void *userp);
|
||||
static void walkexts(Object *self, void (*callback)(void **referer, unsigned int size, void *userp), void *userp);
|
||||
static Object *copy(Object *self, Heap *heap, Error *err);
|
||||
static int hash(Object *self);
|
||||
|
||||
static TypeObject t_list = {
|
||||
.base = (Object) { .type = &t_type, .flags = Object_STATIC },
|
||||
.name = "list",
|
||||
.size = sizeof (ListObject),
|
||||
.copy = copy,
|
||||
.hash = hash,
|
||||
.select = select,
|
||||
.insert = insert,
|
||||
.count = count,
|
||||
.print = print,
|
||||
.walk = walk,
|
||||
.walkexts = walkexts,
|
||||
};
|
||||
|
||||
static int hash(Object *self)
|
||||
{
|
||||
assert(self != NULL);
|
||||
assert(self->type == &t_list);
|
||||
|
||||
ListObject *ls = (ListObject*) self;
|
||||
|
||||
int h = 0;
|
||||
// The hash is the sum of the nested
|
||||
// hashes. It's not a smart solution
|
||||
// but it works for now.
|
||||
for(int i = 0; i < ls->count; i += 1)
|
||||
h += Object_Hash(ls->vals[i]);
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
static Object *copy(Object *self, Heap *heap, Error *err)
|
||||
{
|
||||
(void) heap;
|
||||
(void) err;
|
||||
|
||||
ListObject *ls = (ListObject*) self;
|
||||
ListObject *ls2 = (ListObject*) Object_NewList(ls->count, heap, err);
|
||||
if(ls2 == NULL) return NULL;
|
||||
|
||||
for(int i = 0; i < ls->count; i += 1)
|
||||
{
|
||||
ls2->vals[i] = Object_Copy(ls->vals[i], heap, err);
|
||||
if(err->occurred) return NULL;
|
||||
}
|
||||
|
||||
ls2->count = ls->count;
|
||||
|
||||
return (Object*) ls2;
|
||||
}
|
||||
|
||||
Object *Object_NewList(int capacity, Heap *heap, Error *error)
|
||||
{
|
||||
// Handle default args.
|
||||
if(capacity < 8)
|
||||
capacity = 8;
|
||||
|
||||
// Make the thing.
|
||||
ListObject *obj;
|
||||
{
|
||||
obj = (ListObject*) Heap_Malloc(heap, &t_list, error);
|
||||
|
||||
if(obj == NULL)
|
||||
return NULL;
|
||||
|
||||
obj->count = 0;
|
||||
obj->capacity = capacity;
|
||||
obj->vals = Heap_RawMalloc(heap, sizeof(Object*) * capacity, error);
|
||||
|
||||
if(obj->vals == NULL)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return (Object*) obj;
|
||||
}
|
||||
|
||||
Object *Object_NewList2(int num, Object **items, Heap *heap, Error *error)
|
||||
{
|
||||
assert(num > -1);
|
||||
|
||||
ListObject *list = (ListObject*) Object_NewList(num, heap, error);
|
||||
|
||||
if(list == NULL)
|
||||
return NULL;
|
||||
|
||||
memcpy(list->vals, items, num * sizeof(Object*));
|
||||
list->count = num;
|
||||
|
||||
return (Object*) list;
|
||||
}
|
||||
|
||||
static void walk(Object *self, void (*callback)(Object **referer, void *userp), void *userp)
|
||||
{
|
||||
ListObject *list = (ListObject*) self;
|
||||
|
||||
for(int i = 0; i < list->count; i += 1)
|
||||
callback(&list->vals[i], userp);
|
||||
}
|
||||
|
||||
static void walkexts(Object *self, void (*callback)(void **referer, unsigned int size, void *userp), void *userp)
|
||||
{
|
||||
ListObject *list = (ListObject*) self;
|
||||
|
||||
callback((void**) &list->vals, sizeof(Object) * list->capacity, userp);
|
||||
}
|
||||
|
||||
static Object *select(Object *self, Object *key, Heap *heap, Error *error)
|
||||
{
|
||||
assert(self != NULL);
|
||||
assert(self->type == &t_list);
|
||||
assert(key != NULL);
|
||||
assert(heap != NULL);
|
||||
assert(error != NULL);
|
||||
|
||||
if(!Object_IsInt(key))
|
||||
{
|
||||
Error_Report(error, 0, "Non integer key");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int idx = Object_ToInt(key, error);
|
||||
assert(error->occurred == 0);
|
||||
|
||||
ListObject *list = (ListObject*) self;
|
||||
|
||||
if(idx < 0 || idx >= list->count)
|
||||
{
|
||||
Error_Report(error, 0, "Out of range index");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return list->vals[idx];
|
||||
}
|
||||
|
||||
static unsigned int calc_new_capacity(unsigned int old_capacity)
|
||||
{
|
||||
return old_capacity * 2;
|
||||
}
|
||||
|
||||
static _Bool grow(ListObject *list, Heap *heap, Error *error)
|
||||
{
|
||||
assert(list != NULL);
|
||||
|
||||
int new_capacity = calc_new_capacity(list->capacity);
|
||||
|
||||
Object **vals = Heap_RawMalloc(heap, sizeof(Object*) * new_capacity, error);
|
||||
|
||||
if(vals == NULL)
|
||||
return 0;
|
||||
|
||||
for(int i = 0; i < list->count; i += 1)
|
||||
vals[i] = list->vals[i];
|
||||
|
||||
list->vals = vals;
|
||||
list->capacity = new_capacity;
|
||||
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_list);
|
||||
|
||||
ListObject *list = (ListObject*) self;
|
||||
|
||||
if(!Object_IsInt(key))
|
||||
{
|
||||
Error_Report(error, 0, "Non integer key");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int idx = Object_ToInt(key, error);
|
||||
assert(error->occurred == 0);
|
||||
|
||||
if(idx < 0 || idx > list->count)
|
||||
{
|
||||
Error_Report(error, 0, "Out of range index");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if(idx == list->count)
|
||||
{
|
||||
if(list->count == list->capacity)
|
||||
if(!grow(list, heap, error))
|
||||
return 0;
|
||||
|
||||
list->vals[list->count] = val;
|
||||
list->count += 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
list->vals[idx] = val;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int count(Object *self)
|
||||
{
|
||||
ListObject *list = (ListObject*) self;
|
||||
|
||||
return list->count;
|
||||
}
|
||||
|
||||
static void print(Object *self, FILE *fp)
|
||||
{
|
||||
ListObject *list = (ListObject*) self;
|
||||
|
||||
fprintf(fp, "[");
|
||||
for(int i = 0; i < list->count; i += 1)
|
||||
{
|
||||
Object_Print(list->vals[i], fp);
|
||||
|
||||
if(i+1 < list->count)
|
||||
fprintf(fp, ", ");
|
||||
}
|
||||
fprintf(fp, "]");
|
||||
}
|
||||
@@ -0,0 +1,373 @@
|
||||
|
||||
/* +--------------------------------------------------------------------------+
|
||||
** | _ _ _ |
|
||||
** | | \ | | (_) |
|
||||
** | | \| | ___ _ __ _ |
|
||||
** | | . ` |/ _ \| |/ _` | |
|
||||
** | | |\ | (_) | | (_| | |
|
||||
** | |_| \_|\___/| |\__,_| |
|
||||
** | _/ | |
|
||||
** | |__/ |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | Copyright (c) 2022 Francesco Cozzuto <francesco.cozzuto@gmail.com> |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | This file is part of The Noja Interpreter. |
|
||||
** | |
|
||||
** | The Noja Interpreter is free software: you can redistribute it and/or |
|
||||
** | modify it under the terms of the GNU General Public License as published |
|
||||
** | by the Free Software Foundation, either version 3 of the License, or (at |
|
||||
** | your option) any later version. |
|
||||
** | |
|
||||
** | The Noja Interpreter is distributed in the hope that it will be useful, |
|
||||
** | but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||||
** | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General |
|
||||
** | Public License for more details. |
|
||||
** | |
|
||||
** | You should have received a copy of the GNU General Public License along |
|
||||
** | with The Noja Interpreter. If not, see <http://www.gnu.org/licenses/>. |
|
||||
** +--------------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#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 void print(Object *self, FILE *fp);
|
||||
static void walk(Object *self, void (*callback)(Object **referer, void *userp), void *userp);
|
||||
static void walkexts(Object *self, void (*callback)(void **referer, unsigned int size, void *userp), void *userp);
|
||||
static Object *copy(Object *self, Heap *heap, Error *err);
|
||||
static int hash(Object *self);
|
||||
|
||||
static TypeObject t_map = {
|
||||
.base = (Object) { .type = &t_type, .flags = Object_STATIC },
|
||||
.name = "map",
|
||||
.size = sizeof (MapObject),
|
||||
.copy = copy,
|
||||
.hash = hash,
|
||||
.select = select,
|
||||
.insert = insert,
|
||||
.count = count,
|
||||
.print = print,
|
||||
.walk = walk,
|
||||
.walkexts = walkexts,
|
||||
};
|
||||
|
||||
static inline int calc_capacity(int mapper_size)
|
||||
{
|
||||
return mapper_size * 2.0 / 3.0;
|
||||
}
|
||||
|
||||
static Object *copy(Object *self, Heap *heap, Error *err)
|
||||
{
|
||||
MapObject *m1 = (MapObject*) self;
|
||||
Object *m2 = Object_NewMap(m1->count, heap, err);
|
||||
if(m2 == NULL) return NULL;
|
||||
|
||||
for(int i = 0; i < m1->count; i += 1)
|
||||
{
|
||||
Object *key, *key_cpy;
|
||||
Object *val, *val_cpy;
|
||||
|
||||
key = m1->keys[i];
|
||||
val = m1->vals[i];
|
||||
|
||||
key_cpy = Object_Copy(key, heap, err);
|
||||
if(key_cpy == NULL) return NULL;
|
||||
|
||||
val_cpy = Object_Copy(val, heap, err);
|
||||
if(val_cpy == NULL) return NULL;
|
||||
|
||||
if(!Object_Insert(m2, key_cpy, val_cpy, heap, err))
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return (Object*) m2;
|
||||
}
|
||||
|
||||
static int hash(Object *self)
|
||||
{
|
||||
MapObject *m = (MapObject*) self;
|
||||
|
||||
int h = 0;
|
||||
// The hash of the map is the sum of the
|
||||
// hashes of each key and each item.
|
||||
for(int i = 0; i < m->count; i += 1)
|
||||
h += Object_Hash(m->keys[i])
|
||||
+ Object_Hash(m->vals[i]);
|
||||
return h;
|
||||
}
|
||||
|
||||
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) * mapper_size, 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 void walk(Object *self, void (*callback)(Object **referer, void *userp), void *userp)
|
||||
{
|
||||
MapObject *map = (MapObject*) self;
|
||||
|
||||
for(int i = 0; i < map->count; i += 1)
|
||||
{
|
||||
callback(&map->keys[i], userp);
|
||||
callback(&map->vals[i], userp);
|
||||
}
|
||||
}
|
||||
|
||||
static void walkexts(Object *self, void (*callback)(void **referer, unsigned int size, void *userp), void *userp)
|
||||
{
|
||||
assert(self->type == &t_map);
|
||||
|
||||
MapObject *map = (MapObject*) self;
|
||||
|
||||
int capacity = calc_capacity(map->mapper_size);
|
||||
|
||||
callback((void**) &map->mapper, sizeof(int) * map->mapper_size, userp);
|
||||
callback((void**) &map->keys, sizeof(Object*) * capacity, userp);
|
||||
callback((void**) &map->vals, sizeof(Object*) * capacity, userp);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
unsigned int mask = map->mapper_size - 1;
|
||||
unsigned int hash = Object_Hash(key);
|
||||
unsigned int pert = hash;
|
||||
|
||||
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[k], error))
|
||||
// Found it!
|
||||
return map->vals[k];
|
||||
|
||||
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]);
|
||||
|
||||
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;
|
||||
|
||||
unsigned int mask = map->mapper_size - 1;
|
||||
unsigned int hash = Object_Hash(key);
|
||||
unsigned int pert = hash;
|
||||
|
||||
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[k], error))
|
||||
{
|
||||
// Already inserted.
|
||||
// Overwrite the value.
|
||||
map->vals[k] = 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;
|
||||
}
|
||||
|
||||
static void print(Object *self, FILE *fp)
|
||||
{
|
||||
MapObject *map = (MapObject*) self;
|
||||
|
||||
fprintf(fp, "{");
|
||||
for(int i = 0; i < map->count; i += 1)
|
||||
{
|
||||
Object_Print(map->keys[i], fp);
|
||||
fprintf(fp, ": ");
|
||||
Object_Print(map->vals[i], fp);
|
||||
|
||||
if(i+1 < map->count)
|
||||
fprintf(fp, ", ");
|
||||
}
|
||||
fprintf(fp, "}");
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
|
||||
/* +--------------------------------------------------------------------------+
|
||||
** | _ _ _ |
|
||||
** | | \ | | (_) |
|
||||
** | | \| | ___ _ __ _ |
|
||||
** | | . ` |/ _ \| |/ _` | |
|
||||
** | | |\ | (_) | | (_| | |
|
||||
** | |_| \_|\___/| |\__,_| |
|
||||
** | _/ | |
|
||||
** | |__/ |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | Copyright (c) 2022 Francesco Cozzuto <francesco.cozzuto@gmail.com> |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | This file is part of The Noja Interpreter. |
|
||||
** | |
|
||||
** | The Noja Interpreter is free software: you can redistribute it and/or |
|
||||
** | modify it under the terms of the GNU General Public License as published |
|
||||
** | by the Free Software Foundation, either version 3 of the License, or (at |
|
||||
** | your option) any later version. |
|
||||
** | |
|
||||
** | The Noja Interpreter is distributed in the hope that it will be useful, |
|
||||
** | but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||||
** | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General |
|
||||
** | Public License for more details. |
|
||||
** | |
|
||||
** | You should have received a copy of the GNU General Public License along |
|
||||
** | with The Noja Interpreter. If not, see <http://www.gnu.org/licenses/>. |
|
||||
** +--------------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include "objects.h"
|
||||
|
||||
static _Bool op_eql(Object *self, Object *other);
|
||||
static void print(Object *obj, FILE *fp);
|
||||
static int hash(Object *self);
|
||||
static Object *copy(Object *self, Heap *heap, Error *err);
|
||||
|
||||
static TypeObject t_none = {
|
||||
.base = (Object) { .type = &t_type, .flags = Object_STATIC },
|
||||
.name = "none",
|
||||
.size = sizeof (Object),
|
||||
.hash = hash,
|
||||
.copy = copy,
|
||||
.print = print,
|
||||
.op_eql = op_eql,
|
||||
};
|
||||
|
||||
static Object the_none_object = {
|
||||
.type = &t_none,
|
||||
.flags = Object_STATIC,
|
||||
};
|
||||
|
||||
_Bool Object_IsNone(Object *obj)
|
||||
{
|
||||
return obj == &the_none_object;
|
||||
}
|
||||
|
||||
static int hash(Object *self)
|
||||
{
|
||||
assert(self == &the_none_object);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static Object *copy(Object *self, Heap *heap, Error *err)
|
||||
{
|
||||
(void) heap;
|
||||
(void) err;
|
||||
return self;
|
||||
}
|
||||
|
||||
Object *Object_NewNone(Heap *heap, Error *error)
|
||||
{
|
||||
(void) heap;
|
||||
(void) error;
|
||||
return &the_none_object;
|
||||
}
|
||||
|
||||
static void print(Object *obj, FILE *fp)
|
||||
{
|
||||
assert(fp != NULL);
|
||||
assert(obj != NULL);
|
||||
assert(obj->type == &t_none);
|
||||
|
||||
fprintf(fp, "none");
|
||||
}
|
||||
|
||||
static _Bool op_eql(Object *self, Object *other)
|
||||
{
|
||||
(void) self;
|
||||
|
||||
assert(other->type == &t_none);
|
||||
return 1;
|
||||
}
|
||||
@@ -0,0 +1,234 @@
|
||||
|
||||
/* +--------------------------------------------------------------------------+
|
||||
** | _ _ _ |
|
||||
** | | \ | | (_) |
|
||||
** | | \| | ___ _ __ _ |
|
||||
** | | . ` |/ _ \| |/ _` | |
|
||||
** | | |\ | (_) | | (_| | |
|
||||
** | |_| \_|\___/| |\__,_| |
|
||||
** | _/ | |
|
||||
** | |__/ |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | Copyright (c) 2022 Francesco Cozzuto <francesco.cozzuto@gmail.com> |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | This file is part of The Noja Interpreter. |
|
||||
** | |
|
||||
** | The Noja Interpreter is free software: you can redistribute it and/or |
|
||||
** | modify it under the terms of the GNU General Public License as published |
|
||||
** | by the Free Software Foundation, either version 3 of the License, or (at |
|
||||
** | your option) any later version. |
|
||||
** | |
|
||||
** | The Noja Interpreter is distributed in the hope that it will be useful, |
|
||||
** | but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||||
** | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General |
|
||||
** | Public License for more details. |
|
||||
** | |
|
||||
** | You should have received a copy of the GNU General Public License along |
|
||||
** | with The Noja Interpreter. If not, see <http://www.gnu.org/licenses/>. |
|
||||
** +--------------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include "../utils/defs.h"
|
||||
#include "../utils/hash.h"
|
||||
#include "../utils/utf8.h"
|
||||
#include "objects.h"
|
||||
|
||||
typedef struct {
|
||||
Object base;
|
||||
int count;
|
||||
int bytes;
|
||||
char *body;
|
||||
} StringObject;
|
||||
|
||||
static int hash(Object *self);
|
||||
static int count(Object *self);
|
||||
static Object *copy(Object *self, Heap *heap, Error *err);
|
||||
static void print(Object *obj, FILE *fp);
|
||||
static char *to_string(Object *self, int *size, Heap *heap, Error *err);
|
||||
static _Bool op_eql(Object *self, Object *other);
|
||||
static void walkexts(Object *self, void (*callback)(void **referer, unsigned int size, void *userp), void *userp);
|
||||
static Object *select(Object *self, Object *key, Heap *heap, Error *error);
|
||||
|
||||
static TypeObject t_string = {
|
||||
.base = (Object) { .type = &t_type, .flags = Object_STATIC },
|
||||
.name = "string",
|
||||
.atomic = ATMTP_STRING,
|
||||
.size = sizeof(StringObject),
|
||||
.hash = hash,
|
||||
.count = count,
|
||||
.copy = copy,
|
||||
.print = print,
|
||||
.select = select,
|
||||
.to_string = to_string,
|
||||
.op_eql = op_eql,
|
||||
.walkexts = walkexts,
|
||||
};
|
||||
|
||||
static int char_index_to_offset(StringObject *str, int idx)
|
||||
{
|
||||
if(str->count == str->bytes)
|
||||
return idx;
|
||||
|
||||
// Iterate over a string to find the first byte of
|
||||
// the utf-8 character number [idx].
|
||||
|
||||
int scanned_bytes = 0,
|
||||
last_code_len = 0;
|
||||
|
||||
while(idx > 0)
|
||||
{
|
||||
last_code_len = utf8_sequence_to_utf32_codepoint(str->body + scanned_bytes, str->bytes - scanned_bytes, NULL);
|
||||
scanned_bytes += last_code_len;
|
||||
idx -= 1;
|
||||
|
||||
assert(scanned_bytes <= str->bytes);
|
||||
}
|
||||
|
||||
assert(idx == 0);
|
||||
return scanned_bytes;
|
||||
}
|
||||
|
||||
static Object *select(Object *self, Object *key, Heap *heap, Error *error)
|
||||
{
|
||||
assert(self != NULL && self->type == &t_string);
|
||||
assert(key != NULL && heap != NULL && error != NULL);
|
||||
|
||||
if(!Object_IsInt(key))
|
||||
{
|
||||
Error_Report(error, 0, "Non integer key");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int idx = Object_ToInt(key, error);
|
||||
assert(error->occurred == 0);
|
||||
|
||||
StringObject *str = (StringObject*) self;
|
||||
|
||||
if(idx < 0 || idx >= str->count)
|
||||
{
|
||||
Error_Report(error, 0, "Out of range index");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int byteoffset = char_index_to_offset(str, idx);
|
||||
int codelength = utf8_sequence_to_utf32_codepoint(str->body + byteoffset, str->bytes - byteoffset, NULL);
|
||||
|
||||
return Object_FromString(str->body + byteoffset, codelength, heap, error);
|
||||
}
|
||||
|
||||
static char *to_string(Object *self, int *size, Heap *heap, Error *err)
|
||||
{
|
||||
assert(self != NULL);
|
||||
assert(self->type == &t_string);
|
||||
|
||||
(void) heap;
|
||||
(void) err;
|
||||
|
||||
StringObject *s = (StringObject*) self;
|
||||
|
||||
if(size)
|
||||
*size = s->bytes;
|
||||
|
||||
return s->body;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
int count = utf8_strlen(str, len);
|
||||
|
||||
if(count < 0)
|
||||
{
|
||||
Error_Report(error, 0, "Invalid UTF-8 sequence");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
StringObject *strobj = Heap_Malloc(heap, &t_string, error);
|
||||
|
||||
if(strobj == NULL)
|
||||
return NULL;
|
||||
|
||||
strobj->body = Heap_RawMalloc(heap, len+1, error);
|
||||
strobj->bytes = len;
|
||||
strobj->count = count;
|
||||
|
||||
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->count;
|
||||
}
|
||||
|
||||
static int hash(Object *self)
|
||||
{
|
||||
assert(self != NULL);
|
||||
assert(self->type == &t_string);
|
||||
|
||||
StringObject *strobj = (StringObject*) self;
|
||||
|
||||
return hashbytes((unsigned char*) strobj->body, strobj->count);
|
||||
}
|
||||
|
||||
static Object *copy(Object *self, Heap *heap, Error *err)
|
||||
{
|
||||
assert(self != NULL);
|
||||
assert(self->type == &t_string);
|
||||
assert(heap != NULL);
|
||||
assert(err != NULL);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
static _Bool op_eql(Object *self, Object *other)
|
||||
{
|
||||
assert(self != NULL);
|
||||
assert(self->type == &t_string);
|
||||
assert(other != NULL);
|
||||
assert(other->type == &t_string);
|
||||
|
||||
StringObject *s1 = (StringObject*) self;
|
||||
StringObject *s2 = (StringObject*) other;
|
||||
|
||||
_Bool match = s1->bytes == s2->bytes && !strncmp(s1->body, s2->body, s1->bytes);
|
||||
|
||||
return match;
|
||||
}
|
||||
|
||||
static void print(Object *obj, FILE *fp)
|
||||
{
|
||||
assert(fp != NULL);
|
||||
assert(obj != NULL);
|
||||
assert(obj->type == &t_string);
|
||||
|
||||
StringObject *str = (StringObject*) obj;
|
||||
|
||||
fprintf(fp, "%.*s", str->bytes, str->body);
|
||||
}
|
||||
|
||||
static void walkexts(Object *self, void (*callback)(void **referer, unsigned int size, void *userp), void *userp)
|
||||
{
|
||||
StringObject *str = (StringObject*) self;
|
||||
|
||||
callback((void**) &str->body, str->bytes+1, userp);
|
||||
}
|
||||
@@ -0,0 +1,407 @@
|
||||
|
||||
/* +--------------------------------------------------------------------------+
|
||||
** | _ _ _ |
|
||||
** | | \ | | (_) |
|
||||
** | | \| | ___ _ __ _ |
|
||||
** | | . ` |/ _ \| |/ _` | |
|
||||
** | | |\ | (_) | | (_| | |
|
||||
** | |_| \_|\___/| |\__,_| |
|
||||
** | _/ | |
|
||||
** | |__/ |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | Copyright (c) 2022 Francesco Cozzuto <francesco.cozzuto@gmail.com> |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | This file is part of The Noja Interpreter. |
|
||||
** | |
|
||||
** | The Noja Interpreter is free software: you can redistribute it and/or |
|
||||
** | modify it under the terms of the GNU General Public License as published |
|
||||
** | by the Free Software Foundation, either version 3 of the License, or (at |
|
||||
** | your option) any later version. |
|
||||
** | |
|
||||
** | The Noja Interpreter is distributed in the hope that it will be useful, |
|
||||
** | but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||||
** | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General |
|
||||
** | Public License for more details. |
|
||||
** | |
|
||||
** | You should have received a copy of the GNU General Public License along |
|
||||
** | with The Noja Interpreter. If not, see <http://www.gnu.org/licenses/>. |
|
||||
** +--------------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include "../utils/defs.h"
|
||||
#include "objects.h"
|
||||
|
||||
static _Bool op_eql(Object *self, Object *other);
|
||||
|
||||
TypeObject t_type = {
|
||||
.base = (Object) { .type = &t_type, .flags = Object_STATIC },
|
||||
.name = "type",
|
||||
.size = sizeof (TypeObject),
|
||||
.op_eql = op_eql,
|
||||
};
|
||||
|
||||
static _Bool op_eql(Object *self, Object *other)
|
||||
{
|
||||
return self == other;
|
||||
}
|
||||
|
||||
const char *Object_GetName(const Object *obj)
|
||||
{
|
||||
assert(obj != NULL);
|
||||
|
||||
const TypeObject *type = Object_GetType(obj);
|
||||
assert(type);
|
||||
|
||||
const char *name = type->name;
|
||||
assert(name);
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
const TypeObject *Object_GetType(const Object *obj)
|
||||
{
|
||||
assert(obj != NULL);
|
||||
assert(obj->type != NULL);
|
||||
return obj->type;
|
||||
}
|
||||
|
||||
unsigned int Object_GetSize(const Object *obj, Error *err)
|
||||
{
|
||||
assert(err != NULL);
|
||||
assert(obj != NULL);
|
||||
|
||||
const TypeObject *type = Object_GetType(obj);
|
||||
assert(type);
|
||||
|
||||
return type->size;
|
||||
}
|
||||
|
||||
unsigned int Object_GetDeepSize(const Object *obj, Error *err)
|
||||
{
|
||||
assert(err != NULL);
|
||||
assert(obj != NULL);
|
||||
|
||||
const TypeObject *type = Object_GetType(obj);
|
||||
assert(type);
|
||||
|
||||
if(type->deepsize == NULL)
|
||||
{
|
||||
Error_Report(err, 0, "Object %s doesn't implement %s", Object_GetName(obj), __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return type->deepsize(obj);
|
||||
}
|
||||
|
||||
int Object_Hash(Object *obj)
|
||||
{
|
||||
assert(obj != NULL);
|
||||
const TypeObject *type = Object_GetType(obj);
|
||||
assert(type != NULL);
|
||||
assert(type->hash != NULL);
|
||||
return type->hash(obj);
|
||||
}
|
||||
|
||||
Object *Object_Copy(Object *obj, Heap *heap, Error *err)
|
||||
{
|
||||
assert(err != NULL);
|
||||
assert(obj != NULL);
|
||||
|
||||
const TypeObject *type = Object_GetType(obj);
|
||||
assert(type != NULL);
|
||||
|
||||
if(type->copy == NULL)
|
||||
{
|
||||
Error_Report(err, 0, "Object %s doesn't implement %s", Object_GetName(obj), __func__);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return type->copy(obj, heap, err);
|
||||
}
|
||||
|
||||
int Object_Call(Object *obj, Object **argv, unsigned int argc, Object **rets, unsigned int maxrets, Heap *heap, Error *err)
|
||||
{
|
||||
assert(err != NULL && obj != NULL);
|
||||
|
||||
const TypeObject *type = Object_GetType(obj);
|
||||
assert(type);
|
||||
|
||||
if(type->call == NULL)
|
||||
{
|
||||
Error_Report(err, 0, "Object %s doesn't implement %s", Object_GetName(obj), __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return type->call(obj, argv, argc, rets, maxrets, heap, err);
|
||||
}
|
||||
|
||||
void Object_Print(Object *obj, FILE *fp)
|
||||
{
|
||||
assert(obj != NULL);
|
||||
|
||||
const TypeObject *type = Object_GetType(obj);
|
||||
assert(type);
|
||||
|
||||
if(type->print == NULL)
|
||||
fprintf(fp, "<%s is unprintable>", Object_GetName(obj));
|
||||
else
|
||||
type->print(obj, fp);
|
||||
}
|
||||
|
||||
Object *Object_Select(Object *coll, Object *key, Heap *heap, Error *err)
|
||||
{
|
||||
assert(err);
|
||||
assert(key);
|
||||
assert(coll);
|
||||
|
||||
const TypeObject *type = Object_GetType(coll);
|
||||
assert(type);
|
||||
|
||||
if(type->select == NULL)
|
||||
{
|
||||
Error_Report(err, 0, "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 TypeObject *type = Object_GetType(coll);
|
||||
assert(type);
|
||||
|
||||
if(type->delete == NULL)
|
||||
{
|
||||
Error_Report(err, 0, "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 TypeObject *type = Object_GetType(coll);
|
||||
assert(type);
|
||||
|
||||
if(type->insert == NULL)
|
||||
{
|
||||
Error_Report(err, 0, "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 TypeObject *type = Object_GetType(coll);
|
||||
assert(type);
|
||||
|
||||
if(type->count == NULL)
|
||||
{
|
||||
Error_Report(err, 0, "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 TypeObject *type = Object_GetType(iter);
|
||||
assert(type);
|
||||
|
||||
if(type->next == NULL)
|
||||
{
|
||||
Error_Report(err, 0, "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 TypeObject *type = Object_GetType(iter);
|
||||
assert(type);
|
||||
|
||||
if(type->prev == NULL)
|
||||
{
|
||||
Error_Report(err, 0, "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_IsBool(Object *obj)
|
||||
{
|
||||
assert(obj != NULL);
|
||||
assert(obj->type != NULL);
|
||||
return obj->type->atomic == ATMTP_BOOL;
|
||||
}
|
||||
|
||||
_Bool Object_IsFloat(Object *obj)
|
||||
{
|
||||
assert(obj != NULL);
|
||||
assert(obj->type != NULL);
|
||||
return obj->type->atomic == ATMTP_FLOAT;
|
||||
}
|
||||
|
||||
_Bool Object_IsString(Object *obj)
|
||||
{
|
||||
assert(obj != NULL);
|
||||
assert(obj->type != NULL);
|
||||
return obj->type->atomic == ATMTP_STRING;
|
||||
}
|
||||
|
||||
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 TypeObject *type = Object_GetType(obj);
|
||||
assert(type);
|
||||
|
||||
if(type->to_int == NULL)
|
||||
{
|
||||
Error_Report(err, 0, "Object %s doesn't implement %s", Object_GetName(obj), __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return type->to_int(obj, err);
|
||||
}
|
||||
|
||||
_Bool Object_ToBool(Object *obj, Error *err)
|
||||
{
|
||||
assert(err);
|
||||
assert(obj);
|
||||
|
||||
if(!Object_IsBool(obj))
|
||||
{
|
||||
Error_Report(err, 0, "Object is not a boolean");
|
||||
return 0;
|
||||
}
|
||||
|
||||
const TypeObject *type = Object_GetType(obj);
|
||||
assert(type);
|
||||
|
||||
if(type->to_bool == NULL)
|
||||
{
|
||||
Error_Report(err, 0, "Object %s doesn't implement %s", Object_GetName(obj), __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return type->to_bool(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 TypeObject *type = Object_GetType(obj);
|
||||
assert(type);
|
||||
|
||||
if(type->to_float == NULL)
|
||||
{
|
||||
Error_Report(err, 0, "Object %s doesn't implement %s", Object_GetName(obj), __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return type->to_float(obj, err);
|
||||
}
|
||||
|
||||
const char *Object_ToString(Object *obj, int *size, Heap *heap, Error *err)
|
||||
{
|
||||
assert(err != NULL);
|
||||
assert(obj != NULL);
|
||||
|
||||
if(!Object_IsString(obj))
|
||||
{
|
||||
Error_Report(err, 0, "Object is not a string");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const TypeObject *type = Object_GetType(obj);
|
||||
assert(type);
|
||||
|
||||
if(type->to_string == NULL)
|
||||
{
|
||||
Error_Report(err, 0, "Object %s doesn't implement %s", Object_GetName(obj), __func__);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return type->to_string(obj, size, heap, err);
|
||||
}
|
||||
|
||||
_Bool Object_Compare(Object *obj1, Object *obj2, Error *error)
|
||||
{
|
||||
assert(obj1 != NULL);
|
||||
assert(obj2 != NULL);
|
||||
assert(error != NULL);
|
||||
|
||||
if(obj1->type != obj2->type)
|
||||
return 0;
|
||||
|
||||
if(obj1->type->op_eql == NULL)
|
||||
{
|
||||
Error_Report(error, 0, "Object %s doesn't implement %s", Object_GetName(obj1), __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return obj1->type->op_eql(obj1, obj2);
|
||||
}
|
||||
|
||||
void Object_WalkReferences(Object *parent, void (*callback)(Object **referer, void *userp), void *userp)
|
||||
{
|
||||
assert(parent != NULL);
|
||||
if(parent->type->walk != NULL)
|
||||
parent->type->walk(parent, callback, userp);
|
||||
}
|
||||
|
||||
void Object_WalkExtensions(Object *parent, void (*callback)(void **referer, unsigned int size, void *userp), void *userp)
|
||||
{
|
||||
assert(parent != NULL);
|
||||
if(parent->type->walkexts != NULL)
|
||||
parent->type->walkexts(parent, callback, userp);
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
|
||||
/* +--------------------------------------------------------------------------+
|
||||
** | _ _ _ |
|
||||
** | | \ | | (_) |
|
||||
** | | \| | ___ _ __ _ |
|
||||
** | | . ` |/ _ \| |/ _` | |
|
||||
** | | |\ | (_) | | (_| | |
|
||||
** | |_| \_|\___/| |\__,_| |
|
||||
** | _/ | |
|
||||
** | |__/ |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | Copyright (c) 2022 Francesco Cozzuto <francesco.cozzuto@gmail.com> |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | This file is part of The Noja Interpreter. |
|
||||
** | |
|
||||
** | The Noja Interpreter is free software: you can redistribute it and/or |
|
||||
** | modify it under the terms of the GNU General Public License as published |
|
||||
** | by the Free Software Foundation, either version 3 of the License, or (at |
|
||||
** | your option) any later version. |
|
||||
** | |
|
||||
** | The Noja Interpreter is distributed in the hope that it will be useful, |
|
||||
** | but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||||
** | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General |
|
||||
** | Public License for more details. |
|
||||
** | |
|
||||
** | You should have received a copy of the GNU General Public License along |
|
||||
** | with The Noja Interpreter. If not, see <http://www.gnu.org/licenses/>. |
|
||||
** +--------------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#ifndef OBJECT_H
|
||||
#define OBJECT_H
|
||||
|
||||
#include <dirent.h>
|
||||
#include <stdio.h>
|
||||
#include "../utils/error.h"
|
||||
|
||||
typedef struct TypeObject TypeObject;
|
||||
typedef struct Object Object;
|
||||
typedef struct xHeap Heap;
|
||||
|
||||
struct Object {
|
||||
TypeObject *type;
|
||||
unsigned int flags;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
Object base;
|
||||
Object *new_location;
|
||||
} MovedObject;
|
||||
|
||||
typedef enum {
|
||||
ATMTP_NOTATOMIC = 0,
|
||||
ATMTP_INT,
|
||||
ATMTP_BOOL,
|
||||
ATMTP_FLOAT,
|
||||
ATMTP_STRING,
|
||||
} AtomicType;
|
||||
|
||||
struct TypeObject {
|
||||
|
||||
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);
|
||||
int (*call)(Object *self, Object **argv, unsigned int argc, Object **rets, unsigned int maxrets, Heap *heap, Error *err);
|
||||
void (*print)(Object *self, FILE *fp);
|
||||
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);
|
||||
_Bool (*to_bool)(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);
|
||||
|
||||
// All.
|
||||
void (*walk) (Object *self, void (*callback)(Object **referer, void *userp), void *userp);
|
||||
void (*walkexts)(Object *self, void (*callback)(void **referer, unsigned int size, void *userp), void *userp);
|
||||
};
|
||||
|
||||
enum {
|
||||
Object_STATIC = 1,
|
||||
Object_MOVED = 2,
|
||||
};
|
||||
|
||||
Heap* Heap_New(int size);
|
||||
void Heap_Free(Heap *heap);
|
||||
void* Heap_Malloc (Heap *heap, TypeObject *type, Error *err);
|
||||
void* Heap_RawMalloc(Heap *heap, int size, Error *err);
|
||||
_Bool Heap_StartCollection(Heap *heap, Error *error);
|
||||
_Bool Heap_StopCollection(Heap *heap);
|
||||
void Heap_CollectReference(Object **referer, void *heap);
|
||||
float Heap_GetUsagePercentage(Heap *heap);
|
||||
unsigned int Heap_GetObjectCount(Heap *heap);
|
||||
void *Heap_GetPointer(Heap *heap);
|
||||
unsigned int Heap_GetSize(Heap *heap);
|
||||
|
||||
const TypeObject* 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);
|
||||
void *Object_GetBufferAddrAndSize(Object *obj, int *size, Error *error);
|
||||
int Object_Hash (Object *obj);
|
||||
Object* Object_Copy (Object *obj, Heap *heap, Error *err);
|
||||
int Object_Call (Object *obj, Object **argv, unsigned int argc, Object **rets, unsigned int maxrets, Heap *heap, Error *err);
|
||||
void Object_Print (Object *obj, FILE *fp);
|
||||
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);
|
||||
void Object_WalkReferences(Object *parent, void (*callback)(Object **referer, void *userp), void *userp);
|
||||
void Object_WalkExtensions(Object *parent, void (*callback)(void **referer, unsigned int size, void *userp), void *userp);
|
||||
|
||||
Object* Object_NewMap(int num, Heap *heap, Error *error);
|
||||
Object* Object_NewList(int capacity, Heap *heap, Error *error);
|
||||
Object* Object_NewList2(int num, Object **items, Heap *heap, Error *error);
|
||||
Object* Object_NewNone(Heap *heap, Error *error);
|
||||
Object* Object_NewBuffer(int size, Heap *heap, Error *error);
|
||||
Object* Object_NewClosure(Object *parent, Object *new_map, Heap *heap, Error *error);
|
||||
Object* Object_SliceBuffer(Object *buffer, int offset, int length, Heap *heap, Error *error);
|
||||
|
||||
Object* Object_FromInt (long long int val, Heap *heap, Error *error);
|
||||
Object* Object_FromBool (_Bool 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);
|
||||
Object* Object_FromStream(FILE *fp, Heap *heap, Error *error);
|
||||
Object* Object_FromDIR(DIR *handle, Heap *heap, Error *error);
|
||||
|
||||
_Bool Object_IsNone(Object *obj);
|
||||
_Bool Object_IsInt(Object *obj);
|
||||
_Bool Object_IsBool(Object *obj);
|
||||
_Bool Object_IsFloat(Object *obj);
|
||||
_Bool Object_IsString(Object *obj);
|
||||
_Bool Object_IsBuffer(Object *obj);
|
||||
_Bool Object_IsFile(Object *obj);
|
||||
_Bool Object_IsDir(Object *obj);
|
||||
|
||||
long long int Object_ToInt (Object *obj, Error *err);
|
||||
_Bool Object_ToBool (Object *obj, Error *err);
|
||||
double Object_ToFloat(Object *obj, Error *err);
|
||||
const char *Object_ToString(Object *obj, int *size, Heap *heap, Error *err);
|
||||
DIR *Object_ToDIR(Object *obj, Error *error);
|
||||
FILE *Object_ToStream(Object *obj, Error *error);
|
||||
|
||||
_Bool Object_Compare(Object *obj1, Object *obj2, Error *error);
|
||||
|
||||
|
||||
extern TypeObject t_type;
|
||||
#endif
|
||||
@@ -0,0 +1,189 @@
|
||||
|
||||
/* +--------------------------------------------------------------------------+
|
||||
** | _ _ _ |
|
||||
** | | \ | | (_) |
|
||||
** | | \| | ___ _ __ _ |
|
||||
** | | . ` |/ _ \| |/ _` | |
|
||||
** | | |\ | (_) | | (_| | |
|
||||
** | |_| \_|\___/| |\__,_| |
|
||||
** | _/ | |
|
||||
** | |__/ |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | Copyright (c) 2022 Francesco Cozzuto <francesco.cozzuto@gmail.com> |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | This file is part of The Noja Interpreter. |
|
||||
** | |
|
||||
** | The Noja Interpreter is free software: you can redistribute it and/or |
|
||||
** | modify it under the terms of the GNU General Public License as published |
|
||||
** | by the Free Software Foundation, either version 3 of the License, or (at |
|
||||
** | your option) any later version. |
|
||||
** | |
|
||||
** | The Noja Interpreter is distributed in the hope that it will be useful, |
|
||||
** | but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||||
** | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General |
|
||||
** | Public License for more details. |
|
||||
** | |
|
||||
** | You should have received a copy of the GNU General Public License along |
|
||||
** | with The Noja Interpreter. If not, see <http://www.gnu.org/licenses/>. |
|
||||
** +--------------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include "../utils/defs.h"
|
||||
#include "../objects/objects.h"
|
||||
#include "runtime.h"
|
||||
|
||||
typedef struct {
|
||||
Object base;
|
||||
Runtime *runtime;
|
||||
Executable *exe;
|
||||
int index, argc;
|
||||
Object *closure;
|
||||
} FunctionObject;
|
||||
|
||||
static _Bool free_(Object *self, Error *error)
|
||||
{
|
||||
(void) error;
|
||||
|
||||
FunctionObject *func = (FunctionObject*) self;
|
||||
Executable_Free(func->exe);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void walk(Object *self, void (*callback)(Object **referer, void *userp), void *userp)
|
||||
{
|
||||
FunctionObject *func = (FunctionObject*) self;
|
||||
callback(&func->closure, userp);
|
||||
}
|
||||
|
||||
static int call(Object *self, Object **argv, unsigned int argc, Object **rets, unsigned int maxretc, Heap *heap, Error *error)
|
||||
{
|
||||
assert(self != NULL && heap != NULL && error != NULL);
|
||||
|
||||
FunctionObject *func = (FunctionObject*) self;
|
||||
|
||||
assert(func->exe != NULL);
|
||||
assert(func->argc >= 0);
|
||||
assert(func->index >= 0);
|
||||
|
||||
// Make sure the right amount of arguments is provided.
|
||||
|
||||
Object **argv2;
|
||||
|
||||
int expected_argc = func->argc;
|
||||
|
||||
if(expected_argc < (int) argc)
|
||||
{
|
||||
// Nothing to be done. By using
|
||||
// the right argc the additional
|
||||
// arguments are ignored implicitly.
|
||||
argv2 = argv;
|
||||
}
|
||||
else if(expected_argc > (int) argc)
|
||||
{
|
||||
// Some arguments are missing.
|
||||
argv2 = malloc(sizeof(Object*) * expected_argc);
|
||||
|
||||
if(argv2 == NULL)
|
||||
{
|
||||
Error_Report(error, 1, "No memory");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Copy the provided arguments.
|
||||
for(int i = 0; i < (int) argc; i += 1)
|
||||
argv2[i] = argv[i];
|
||||
|
||||
// Set the unspecified arguments to none.
|
||||
for(int i = argc; i < expected_argc; i += 1)
|
||||
{
|
||||
argv2[i] = Object_NewNone(heap, error);
|
||||
|
||||
if(argv2[i] == NULL)
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
// The right amount of arguments was provided.
|
||||
argv2 = argv;
|
||||
|
||||
int retc = run(func->runtime, error, func->exe, func->index, func->closure, argv2, expected_argc, rets, maxretc);
|
||||
|
||||
// NOTE: Every object reference is invalidated from here.
|
||||
|
||||
if(argv2 != argv)
|
||||
free(argv2);
|
||||
|
||||
return retc;
|
||||
}
|
||||
|
||||
static TypeObject t_func = {
|
||||
.base = (Object) { .type = &t_type, .flags = Object_STATIC },
|
||||
.name = "function",
|
||||
.size = sizeof (FunctionObject),
|
||||
.call = call,
|
||||
.walk = walk,
|
||||
.free = free_,
|
||||
};
|
||||
|
||||
/* Symbol: Object_FromNojaFunction
|
||||
*
|
||||
* Creates an object from a noja executable structure.
|
||||
*
|
||||
* Args:
|
||||
* - runtime: The reference to an instanciated Runtime.
|
||||
*
|
||||
* - exe: A noja executable.
|
||||
*
|
||||
* - index: The index of the first bytecode instruction
|
||||
* of the noja function within the executable.
|
||||
*
|
||||
* - argc: The number of arguments the function expects.
|
||||
* It must be positive (unlike [Object_FromNativeFunction],
|
||||
* where -1 means variadic).
|
||||
*
|
||||
* - closure: An object containing variables that will be
|
||||
* accessible from the noja function other than
|
||||
* the ones that will be defined inside it.
|
||||
*
|
||||
* - heap: The heap that will be used to allocate the object.
|
||||
* It can't be NULL.
|
||||
*
|
||||
* - error: Output parameter where error information is stored.
|
||||
* It can't be NULL.
|
||||
*
|
||||
* Returns:
|
||||
* The newly created object. If an error occurred, NULL is returned
|
||||
* and information about the error is stored in the [error] argument.
|
||||
*/
|
||||
Object *Object_FromNojaFunction(Runtime *runtime, Executable *exe, int index, int argc, Object *closure, Heap *heap, Error *error)
|
||||
{
|
||||
assert(runtime != NULL);
|
||||
assert(exe != NULL);
|
||||
assert(index >= 0);
|
||||
assert(argc >= 0);
|
||||
assert(heap != NULL);
|
||||
assert(error != NULL);
|
||||
|
||||
FunctionObject *func = (FunctionObject*) Heap_Malloc(heap, &t_func, error);
|
||||
|
||||
if(func == NULL)
|
||||
return NULL;
|
||||
|
||||
Executable *exe_copy = Executable_Copy(exe);
|
||||
|
||||
if(exe_copy == NULL)
|
||||
{
|
||||
Error_Report(error, 1, "Failed to copy executable");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
func->runtime = runtime;
|
||||
func->exe = exe_copy;
|
||||
func->index = index;
|
||||
func->argc = argc;
|
||||
func->closure = closure;
|
||||
|
||||
return (Object*) func;
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
|
||||
/* +--------------------------------------------------------------------------+
|
||||
** | _ _ _ |
|
||||
** | | \ | | (_) |
|
||||
** | | \| | ___ _ __ _ |
|
||||
** | | . ` |/ _ \| |/ _` | |
|
||||
** | | |\ | (_) | | (_| | |
|
||||
** | |_| \_|\___/| |\__,_| |
|
||||
** | _/ | |
|
||||
** | |__/ |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | Copyright (c) 2022 Francesco Cozzuto <francesco.cozzuto@gmail.com> |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | This file is part of The Noja Interpreter. |
|
||||
** | |
|
||||
** | The Noja Interpreter is free software: you can redistribute it and/or |
|
||||
** | modify it under the terms of the GNU General Public License as published |
|
||||
** | by the Free Software Foundation, either version 3 of the License, or (at |
|
||||
** | your option) any later version. |
|
||||
** | |
|
||||
** | The Noja Interpreter is distributed in the hope that it will be useful, |
|
||||
** | but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||||
** | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General |
|
||||
** | Public License for more details. |
|
||||
** | |
|
||||
** | You should have received a copy of the GNU General Public License along |
|
||||
** | with The Noja Interpreter. If not, see <http://www.gnu.org/licenses/>. |
|
||||
** +--------------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
/*
|
||||
* WHAT IS THIS FILE?
|
||||
* This file implements an object that makes it possible
|
||||
* to call native functions from within noja code.
|
||||
*
|
||||
*/
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include "../utils/defs.h"
|
||||
#include "../objects/objects.h"
|
||||
#include "runtime.h"
|
||||
|
||||
typedef struct {
|
||||
Object base;
|
||||
Runtime *runtime;
|
||||
int (*callback)(Runtime *runtime, Object **argv, unsigned int argc, Object **rets, unsigned int maxretc, Error *error);
|
||||
int argc;
|
||||
} NativeFunctionObject;
|
||||
|
||||
static int call(Object *self, Object **argv, unsigned int argc, Object **rets, unsigned int maxretc, Heap *heap, Error *error)
|
||||
{
|
||||
assert(self != NULL);
|
||||
assert(heap != NULL);
|
||||
assert(error != NULL);
|
||||
|
||||
NativeFunctionObject *func = (NativeFunctionObject*) self;
|
||||
|
||||
// If the function isn't variadic, make sure
|
||||
// the right amount of arguments is provided.
|
||||
|
||||
Object **argv2;
|
||||
int argc2;
|
||||
|
||||
int expected_argc = func->argc;
|
||||
|
||||
if(expected_argc < 0 || expected_argc == (int) argc)
|
||||
{
|
||||
// The function is variadic or the right
|
||||
// amount of arguments was provided.
|
||||
argv2 = argv;
|
||||
argc2 = argc;
|
||||
}
|
||||
else if(expected_argc < (int) argc)
|
||||
{
|
||||
// Nothing to be done. By using
|
||||
// the right argc the additional
|
||||
// arguments are ignored implicitly.
|
||||
argv2 = argv;
|
||||
argc2 = expected_argc;
|
||||
}
|
||||
else if(expected_argc > (int) argc)
|
||||
{
|
||||
// Some arguments are missing.
|
||||
argv2 = malloc(sizeof(Object*) * expected_argc);
|
||||
argc2 = expected_argc;
|
||||
|
||||
if(argv2 == NULL)
|
||||
{
|
||||
Error_Report(error, 1, "No memory");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Copy the provided arguments.
|
||||
for(int i = 0; i < (int) argc; i += 1)
|
||||
argv2[i] = argv[i];
|
||||
|
||||
// Set the unspecified arguments to none.
|
||||
for(int i = argc; i < expected_argc; i += 1)
|
||||
{
|
||||
argv2[i] = Object_NewNone(heap, error);
|
||||
|
||||
if(argv2[i] == NULL)
|
||||
{
|
||||
free(argv2);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else UNREACHABLE;
|
||||
|
||||
assert(func->callback != NULL);
|
||||
int retc = func->callback(func->runtime, argv2, argc2, rets, maxretc, error);
|
||||
|
||||
// NOTE: Since the callback may have executed some bytecode, a GC
|
||||
// cycle may have been triggered, therefore we must assume
|
||||
// every object reference that was locally saved is invalidated
|
||||
// from here (the returned object is good tho).
|
||||
|
||||
if(argv2 != argv)
|
||||
free(argv2);
|
||||
|
||||
return retc;
|
||||
}
|
||||
|
||||
static TypeObject t_nfunc = {
|
||||
.base = (Object) { .type = &t_type, .flags = Object_STATIC },
|
||||
.name = "native function",
|
||||
.size = sizeof (NativeFunctionObject),
|
||||
.call = call,
|
||||
};
|
||||
|
||||
/* Symbol: Object_FromNativeFunction
|
||||
*
|
||||
* Creates an object from a function pointer.
|
||||
*
|
||||
* Args:
|
||||
* - runtime: The reference to an instanciated Runtime. This must be
|
||||
* provided so that the callback can also access it.
|
||||
*
|
||||
* - callback: The native function to be executed when this object
|
||||
* is called.
|
||||
*
|
||||
* - argc: The number of arguments the function expects. If -1 is
|
||||
* provided, then the function is considered to be variadic.
|
||||
*
|
||||
* - heap: The heap that will be used to allocate the object.
|
||||
* It can't be NULL.
|
||||
*
|
||||
* - error: Output parameter where error information is stored.
|
||||
* It can't be NULL.
|
||||
*
|
||||
* Returns:
|
||||
* The newly created object. If an error occurred, NULL is returned
|
||||
* and information about the error is stored in the [error] argument.
|
||||
*/
|
||||
Object *Object_FromNativeFunction(Runtime *runtime, int (*callback)(Runtime*, Object**, unsigned int, Object**, unsigned int, Error*), int argc, Heap *heap, Error *error)
|
||||
{
|
||||
assert(callback != NULL);
|
||||
|
||||
NativeFunctionObject *func = (NativeFunctionObject*) Heap_Malloc(heap, &t_nfunc, error);
|
||||
|
||||
if(func == NULL)
|
||||
return NULL;
|
||||
|
||||
func->runtime = runtime;
|
||||
func->callback = callback;
|
||||
func->argc = argc;
|
||||
|
||||
return (Object*) func;
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
|
||||
/* +--------------------------------------------------------------------------+
|
||||
** | _ _ _ |
|
||||
** | | \ | | (_) |
|
||||
** | | \| | ___ _ __ _ |
|
||||
** | | . ` |/ _ \| |/ _` | |
|
||||
** | | |\ | (_) | | (_| | |
|
||||
** | |_| \_|\___/| |\__,_| |
|
||||
** | _/ | |
|
||||
** | |__/ |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | Copyright (c) 2022 Francesco Cozzuto <francesco.cozzuto@gmail.com> |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | This file is part of The Noja Interpreter. |
|
||||
** | |
|
||||
** | The Noja Interpreter is free software: you can redistribute it and/or |
|
||||
** | modify it under the terms of the GNU General Public License as published |
|
||||
** | by the Free Software Foundation, either version 3 of the License, or (at |
|
||||
** | your option) any later version. |
|
||||
** | |
|
||||
** | The Noja Interpreter is distributed in the hope that it will be useful, |
|
||||
** | but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||||
** | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General |
|
||||
** | Public License for more details. |
|
||||
** | |
|
||||
** | You should have received a copy of the GNU General Public License along |
|
||||
** | with The Noja Interpreter. If not, see <http://www.gnu.org/licenses/>. |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | WHAT IS THIS FILE? |
|
||||
** | This file implements the "static map" object. The static map object |
|
||||
** | behaves like a read-only "map". (Note that "implementing an object" means|
|
||||
** | a very specific thing in this interpreter. If you didn't know, check the |
|
||||
** | src/objects folder.) |
|
||||
** | |
|
||||
** | THE STATIC MAP OBJECT |
|
||||
** | The statis map is a read-only collection of objects. You can see it as |
|
||||
** | an interface for static arrays. You can define an array of |
|
||||
** | `StaticMapSlot`s and then wrap it in this object. When the map is |
|
||||
** | accessed, a lookup is performed into the array. Something to note is that|
|
||||
** | the array is converted to noja objects lazily when they are accessed, |
|
||||
** | which makes the start-up times lower than a general purpose map. |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | NOTES: |
|
||||
** | - Only strings can be keys. There is no intrinsic reason why |
|
||||
** | it should be like that, it's just simpler. |
|
||||
** +--------------------------------------------------------------------------+
|
||||
*/
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include "../runtime/runtime.h"
|
||||
#include "../utils/defs.h"
|
||||
#include "../objects/objects.h"
|
||||
|
||||
typedef struct {
|
||||
Object base;
|
||||
Runtime *runt;
|
||||
const StaticMapSlot *slots;
|
||||
} StaticMapObject;
|
||||
|
||||
static Object *select(Object *self, Object *key, Heap *heap, Error *err);
|
||||
static Object *copy(Object *self, Heap *heap, Error *err);
|
||||
static int hash(Object *self);
|
||||
|
||||
static TypeObject t_staticmap = {
|
||||
.base = (Object) { .type = &t_type, .flags = Object_STATIC },
|
||||
.name = "static map",
|
||||
.size = sizeof (StaticMapObject),
|
||||
.copy = copy,
|
||||
.hash = hash,
|
||||
.select = select,
|
||||
};
|
||||
|
||||
static Object *copy(Object *self, Heap *heap, Error *err)
|
||||
{
|
||||
(void) heap;
|
||||
(void) err;
|
||||
return self;
|
||||
}
|
||||
|
||||
static int hash(Object *self)
|
||||
{
|
||||
(void) self;
|
||||
return 0;
|
||||
}
|
||||
|
||||
Object *Object_NewStaticMap(const StaticMapSlot *slots, Runtime *runt, Error *error)
|
||||
{
|
||||
Heap *heap = Runtime_GetHeap(runt);
|
||||
|
||||
// Make the thing.
|
||||
StaticMapObject *obj = (StaticMapObject*) Heap_Malloc(heap, &t_staticmap, error);
|
||||
{
|
||||
if(obj == 0)
|
||||
return 0;
|
||||
|
||||
obj->runt = runt;
|
||||
obj->slots = slots;
|
||||
}
|
||||
|
||||
return (Object*) obj;
|
||||
}
|
||||
|
||||
static Object *select(Object *self, Object *key, Heap *heap, Error *error)
|
||||
{
|
||||
assert(self != NULL);
|
||||
assert(self->type == &t_staticmap);
|
||||
assert(key != NULL);
|
||||
assert(heap != NULL);
|
||||
assert(error != NULL);
|
||||
|
||||
StaticMapObject *map = (StaticMapObject*) self;
|
||||
|
||||
if(!Object_IsString(key))
|
||||
return NULL;
|
||||
|
||||
const char *name = Object_ToString(key, NULL, heap, error);
|
||||
|
||||
if(map->slots == NULL)
|
||||
return NULL;
|
||||
|
||||
for(int i = 0; map->slots[i].name != NULL; i += 1)
|
||||
if(!strcmp(name, map->slots[i].name))
|
||||
{
|
||||
StaticMapSlot slot = map->slots[i];
|
||||
Object *obj;
|
||||
switch(slot.kind)
|
||||
{
|
||||
case SM_BOOL: return Object_FromBool(slot.as_bool, heap, error);
|
||||
case SM_INT: return Object_FromInt(slot.as_int, heap, error);
|
||||
case SM_FLOAT: return Object_FromFloat(slot.as_float, heap, error);
|
||||
case SM_FUNCT: return Object_FromNativeFunction(map->runt, slot.as_funct, slot.argc, heap, error);
|
||||
case SM_STRING: return Object_FromString(slot.as_string, slot.length, heap, error);
|
||||
case SM_SMAP: return Object_NewStaticMap(slot.as_smap, map->runt, error);
|
||||
case SM_NONE: return Object_NewNone(heap, error);
|
||||
case SM_TYPE: return (Object*) slot.as_type;
|
||||
default: assert(0); break;
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,100 @@
|
||||
|
||||
/* +--------------------------------------------------------------------------+
|
||||
** | _ _ _ |
|
||||
** | | \ | | (_) |
|
||||
** | | \| | ___ _ __ _ |
|
||||
** | | . ` |/ _ \| |/ _` | |
|
||||
** | | |\ | (_) | | (_| | |
|
||||
** | |_| \_|\___/| |\__,_| |
|
||||
** | _/ | |
|
||||
** | |__/ |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | Copyright (c) 2022 Francesco Cozzuto <francesco.cozzuto@gmail.com> |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | This file is part of The Noja Interpreter. |
|
||||
** | |
|
||||
** | The Noja Interpreter is free software: you can redistribute it and/or |
|
||||
** | modify it under the terms of the GNU General Public License as published |
|
||||
** | by the Free Software Foundation, either version 3 of the License, or (at |
|
||||
** | your option) any later version. |
|
||||
** | |
|
||||
** | The Noja Interpreter is distributed in the hope that it will be useful, |
|
||||
** | but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||||
** | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General |
|
||||
** | Public License for more details. |
|
||||
** | |
|
||||
** | You should have received a copy of the GNU General Public License along |
|
||||
** | with The Noja Interpreter. If not, see <http://www.gnu.org/licenses/>. |
|
||||
** +--------------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#ifndef RUNTIME_H
|
||||
#define RUNTIME_H
|
||||
|
||||
#include <stdio.h> // meh.. just for the definition of FILE.
|
||||
#include "../utils/error.h"
|
||||
#include "../utils/stack.h"
|
||||
#include "../objects/objects.h"
|
||||
#include "../common/executable.h"
|
||||
|
||||
typedef struct xRuntime Runtime;
|
||||
typedef struct xSnapshot Snapshot;
|
||||
|
||||
Runtime* Runtime_New(int stack_size, int heap_size, void *callback_userp, _Bool (*callback_addr)(Runtime*, void*));
|
||||
Runtime* Runtime_New2(int stack_size, Heap *heap, _Bool free_heap, void *callback_userp, _Bool (*callback_addr)(Runtime*, void*));
|
||||
void Runtime_Free(Runtime *runtime);
|
||||
_Bool Runtime_Pop(Runtime *runtime, Error *error, unsigned int n);
|
||||
_Bool Runtime_Push(Runtime *runtime, Error *error, Object *obj);
|
||||
Heap* Runtime_GetHeap(Runtime *runtime);
|
||||
Stack* Runtime_GetStack(Runtime *runtime);
|
||||
Object* Runtime_GetBuiltins(Runtime *runtime);
|
||||
void Runtime_SetBuiltins(Runtime *runtime, Object *builtins);
|
||||
int Runtime_GetCurrentIndex(Runtime *runtime);
|
||||
Executable *Runtime_GetCurrentExecutable(Runtime *runtime);
|
||||
Snapshot *Snapshot_New(Runtime *runtime);
|
||||
void Snapshot_Free(Snapshot *snapshot);
|
||||
void Snapshot_Print(Snapshot *snapshot, FILE *fp);
|
||||
int run(Runtime *runtime, Error *error, Executable *exe, int index, Object *closure, Object **argv, int argc, Object **rets, int maxretc);
|
||||
|
||||
typedef enum {
|
||||
SM_END,
|
||||
SM_BOOL,
|
||||
SM_INT,
|
||||
SM_FLOAT,
|
||||
SM_FUNCT,
|
||||
SM_STRING,
|
||||
SM_SMAP,
|
||||
SM_NONE,
|
||||
SM_TYPE,
|
||||
} StaticMapSlotKind;
|
||||
|
||||
typedef struct StaticMapSlot StaticMapSlot;
|
||||
|
||||
struct StaticMapSlot {
|
||||
const char *name;
|
||||
StaticMapSlotKind kind;
|
||||
union {
|
||||
const StaticMapSlot *as_smap;
|
||||
const char *as_string;
|
||||
_Bool as_bool;
|
||||
long long int as_int;
|
||||
double as_float;
|
||||
int (*as_funct)(Runtime*, Object**, unsigned int, Object**, unsigned int, Error*);
|
||||
TypeObject *as_type;
|
||||
};
|
||||
union { int argc; int length; };
|
||||
};
|
||||
|
||||
Object *Object_NewStaticMap(const StaticMapSlot *slots, Runtime *runt, Error *error);
|
||||
Object *Object_FromNojaFunction(Runtime *runtime, Executable *exe, int index, int argc, Object *closure, Heap *heap, Error *error);
|
||||
Object *Object_FromNativeFunction(Runtime *runtime, int (*callback)(Runtime*, Object**, unsigned int, Object**, unsigned int, Error*), int argc, Heap *heap, Error *error);
|
||||
typedef struct {
|
||||
Error base;
|
||||
Runtime *runtime;
|
||||
Snapshot *snapshot;
|
||||
} RuntimeError;
|
||||
|
||||
void RuntimeError_Init(RuntimeError *error, Runtime *runtime);
|
||||
void RuntimeError_Free(RuntimeError *error);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,59 @@
|
||||
|
||||
/* +--------------------------------------------------------------------------+
|
||||
** | _ _ _ |
|
||||
** | | \ | | (_) |
|
||||
** | | \| | ___ _ __ _ |
|
||||
** | | . ` |/ _ \| |/ _` | |
|
||||
** | | |\ | (_) | | (_| | |
|
||||
** | |_| \_|\___/| |\__,_| |
|
||||
** | _/ | |
|
||||
** | |__/ |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | Copyright (c) 2022 Francesco Cozzuto <francesco.cozzuto@gmail.com> |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | This file is part of The Noja Interpreter. |
|
||||
** | |
|
||||
** | The Noja Interpreter is free software: you can redistribute it and/or |
|
||||
** | modify it under the terms of the GNU General Public License as published |
|
||||
** | by the Free Software Foundation, either version 3 of the License, or (at |
|
||||
** | your option) any later version. |
|
||||
** | |
|
||||
** | The Noja Interpreter is distributed in the hope that it will be useful, |
|
||||
** | but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||||
** | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General |
|
||||
** | Public License for more details. |
|
||||
** | |
|
||||
** | You should have received a copy of the GNU General Public License along |
|
||||
** | with The Noja Interpreter. If not, see <http://www.gnu.org/licenses/>. |
|
||||
** +--------------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include "runtime.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);
|
||||
}
|
||||
@@ -0,0 +1,247 @@
|
||||
|
||||
/* +--------------------------------------------------------------------------+
|
||||
** | _ _ _ |
|
||||
** | | \ | | (_) |
|
||||
** | | \| | ___ _ __ _ |
|
||||
** | | . ` |/ _ \| |/ _` | |
|
||||
** | | |\ | (_) | | (_| | |
|
||||
** | |_| \_|\___/| |\__,_| |
|
||||
** | _/ | |
|
||||
** | |__/ |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | Copyright (c) 2022 Francesco Cozzuto <francesco.cozzuto@gmail.com> |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | This file is part of The Noja Interpreter. |
|
||||
** | |
|
||||
** | The Noja Interpreter is free software: you can redistribute it and/or |
|
||||
** | modify it under the terms of the GNU General Public License as published |
|
||||
** | by the Free Software Foundation, either version 3 of the License, or (at |
|
||||
** | your option) any later version. |
|
||||
** | |
|
||||
** | The Noja Interpreter is distributed in the hope that it will be useful, |
|
||||
** | but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||||
** | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General |
|
||||
** | Public License for more details. |
|
||||
** | |
|
||||
** | You should have received a copy of the GNU General Public License along |
|
||||
** | with The Noja Interpreter. If not, see <http://www.gnu.org/licenses/>. |
|
||||
** +--------------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#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);
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
|
||||
/* +--------------------------------------------------------------------------+
|
||||
** | _ _ _ |
|
||||
** | | \ | | (_) |
|
||||
** | | \| | ___ _ __ _ |
|
||||
** | | . ` |/ _ \| |/ _` | |
|
||||
** | | |\ | (_) | | (_| | |
|
||||
** | |_| \_|\___/| |\__,_| |
|
||||
** | _/ | |
|
||||
** | |__/ |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | Copyright (c) 2022 Francesco Cozzuto <francesco.cozzuto@gmail.com> |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | This file is part of The Noja Interpreter. |
|
||||
** | |
|
||||
** | The Noja Interpreter is free software: you can redistribute it and/or |
|
||||
** | modify it under the terms of the GNU General Public License as published |
|
||||
** | by the Free Software Foundation, either version 3 of the License, or (at |
|
||||
** | your option) any later version. |
|
||||
** | |
|
||||
** | The Noja Interpreter is distributed in the hope that it will be useful, |
|
||||
** | but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||||
** | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General |
|
||||
** | Public License for more details. |
|
||||
** | |
|
||||
** | You should have received a copy of the GNU General Public License along |
|
||||
** | with The Noja Interpreter. If not, see <http://www.gnu.org/licenses/>. |
|
||||
** +--------------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#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
|
||||
@@ -0,0 +1,212 @@
|
||||
|
||||
/* +--------------------------------------------------------------------------+
|
||||
** | _ _ _ |
|
||||
** | | \ | | (_) |
|
||||
** | | \| | ___ _ __ _ |
|
||||
** | | . ` |/ _ \| |/ _` | |
|
||||
** | | |\ | (_) | | (_| | |
|
||||
** | |_| \_|\___/| |\__,_| |
|
||||
** | _/ | |
|
||||
** | |__/ |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | Copyright (c) 2022 Francesco Cozzuto <francesco.cozzuto@gmail.com> |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | This file is part of The Noja Interpreter. |
|
||||
** | |
|
||||
** | The Noja Interpreter is free software: you can redistribute it and/or |
|
||||
** | modify it under the terms of the GNU General Public License as published |
|
||||
** | by the Free Software Foundation, either version 3 of the License, or (at |
|
||||
** | your option) any later version. |
|
||||
** | |
|
||||
** | The Noja Interpreter is distributed in the hope that it will be useful, |
|
||||
** | but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||||
** | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General |
|
||||
** | Public License for more details. |
|
||||
** | |
|
||||
** | You should have received a copy of the GNU General Public License along |
|
||||
** | with The Noja Interpreter. If not, see <http://www.gnu.org/licenses/>. |
|
||||
** +--------------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#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;
|
||||
}
|
||||
}
|
||||
|
||||
BPAlloc *BucketList_GetAlloc(BucketList *blist)
|
||||
{
|
||||
return blist->alloc;
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
|
||||
/* +--------------------------------------------------------------------------+
|
||||
** | _ _ _ |
|
||||
** | | \ | | (_) |
|
||||
** | | \| | ___ _ __ _ |
|
||||
** | | . ` |/ _ \| |/ _` | |
|
||||
** | | |\ | (_) | | (_| | |
|
||||
** | |_| \_|\___/| |\__,_| |
|
||||
** | _/ | |
|
||||
** | |__/ |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | Copyright (c) 2022 Francesco Cozzuto <francesco.cozzuto@gmail.com> |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | This file is part of The Noja Interpreter. |
|
||||
** | |
|
||||
** | The Noja Interpreter is free software: you can redistribute it and/or |
|
||||
** | modify it under the terms of the GNU General Public License as published |
|
||||
** | by the Free Software Foundation, either version 3 of the License, or (at |
|
||||
** | your option) any later version. |
|
||||
** | |
|
||||
** | The Noja Interpreter is distributed in the hope that it will be useful, |
|
||||
** | but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||||
** | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General |
|
||||
** | Public License for more details. |
|
||||
** | |
|
||||
** | You should have received a copy of the GNU General Public License along |
|
||||
** | with The Noja Interpreter. If not, see <http://www.gnu.org/licenses/>. |
|
||||
** +--------------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#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);
|
||||
BPAlloc *BucketList_GetAlloc(BucketList *blist);
|
||||
#endif
|
||||
@@ -0,0 +1,47 @@
|
||||
|
||||
/* +--------------------------------------------------------------------------+
|
||||
** | _ _ _ |
|
||||
** | | \ | | (_) |
|
||||
** | | \| | ___ _ __ _ |
|
||||
** | | . ` |/ _ \| |/ _` | |
|
||||
** | | |\ | (_) | | (_| | |
|
||||
** | |_| \_|\___/| |\__,_| |
|
||||
** | _/ | |
|
||||
** | |__/ |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | Copyright (c) 2022 Francesco Cozzuto <francesco.cozzuto@gmail.com> |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | This file is part of The Noja Interpreter. |
|
||||
** | |
|
||||
** | The Noja Interpreter is free software: you can redistribute it and/or |
|
||||
** | modify it under the terms of the GNU General Public License as published |
|
||||
** | by the Free Software Foundation, either version 3 of the License, or (at |
|
||||
** | your option) any later version. |
|
||||
** | |
|
||||
** | The Noja Interpreter is distributed in the hope that it will be useful, |
|
||||
** | but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||||
** | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General |
|
||||
** | Public License for more details. |
|
||||
** | |
|
||||
** | You should have received a copy of the GNU General Public License along |
|
||||
** | with The Noja Interpreter. If not, see <http://www.gnu.org/licenses/>. |
|
||||
** +--------------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#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))
|
||||
@@ -0,0 +1,118 @@
|
||||
|
||||
/* +--------------------------------------------------------------------------+
|
||||
** | _ _ _ |
|
||||
** | | \ | | (_) |
|
||||
** | | \| | ___ _ __ _ |
|
||||
** | | . ` |/ _ \| |/ _` | |
|
||||
** | | |\ | (_) | | (_| | |
|
||||
** | |_| \_|\___/| |\__,_| |
|
||||
** | _/ | |
|
||||
** | |__/ |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | Copyright (c) 2022 Francesco Cozzuto <francesco.cozzuto@gmail.com> |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | This file is part of The Noja Interpreter. |
|
||||
** | |
|
||||
** | The Noja Interpreter is free software: you can redistribute it and/or |
|
||||
** | modify it under the terms of the GNU General Public License as published |
|
||||
** | by the Free Software Foundation, either version 3 of the License, or (at |
|
||||
** | your option) any later version. |
|
||||
** | |
|
||||
** | The Noja Interpreter is distributed in the hope that it will be useful, |
|
||||
** | but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||||
** | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General |
|
||||
** | Public License for more details. |
|
||||
** | |
|
||||
** | You should have received a copy of the GNU General Public License along |
|
||||
** | with The Noja Interpreter. If not, see <http://www.gnu.org/licenses/>. |
|
||||
** +--------------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#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);
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
|
||||
/* +--------------------------------------------------------------------------+
|
||||
** | _ _ _ |
|
||||
** | | \ | | (_) |
|
||||
** | | \| | ___ _ __ _ |
|
||||
** | | . ` |/ _ \| |/ _` | |
|
||||
** | | |\ | (_) | | (_| | |
|
||||
** | |_| \_|\___/| |\__,_| |
|
||||
** | _/ | |
|
||||
** | |__/ |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | Copyright (c) 2022 Francesco Cozzuto <francesco.cozzuto@gmail.com> |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | This file is part of The Noja Interpreter. |
|
||||
** | |
|
||||
** | The Noja Interpreter is free software: you can redistribute it and/or |
|
||||
** | modify it under the terms of the GNU General Public License as published |
|
||||
** | by the Free Software Foundation, either version 3 of the License, or (at |
|
||||
** | your option) any later version. |
|
||||
** | |
|
||||
** | The Noja Interpreter is distributed in the hope that it will be useful, |
|
||||
** | but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||||
** | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General |
|
||||
** | Public License for more details. |
|
||||
** | |
|
||||
** | You should have received a copy of the GNU General Public License along |
|
||||
** | with The Noja Interpreter. If not, see <http://www.gnu.org/licenses/>. |
|
||||
** +--------------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#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
|
||||
@@ -0,0 +1,49 @@
|
||||
|
||||
/* +--------------------------------------------------------------------------+
|
||||
** | _ _ _ |
|
||||
** | | \ | | (_) |
|
||||
** | | \| | ___ _ __ _ |
|
||||
** | | . ` |/ _ \| |/ _` | |
|
||||
** | | |\ | (_) | | (_| | |
|
||||
** | |_| \_|\___/| |\__,_| |
|
||||
** | _/ | |
|
||||
** | |__/ |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | Copyright (c) 2022 Francesco Cozzuto <francesco.cozzuto@gmail.com> |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | This file is part of The Noja Interpreter. |
|
||||
** | |
|
||||
** | The Noja Interpreter is free software: you can redistribute it and/or |
|
||||
** | modify it under the terms of the GNU General Public License as published |
|
||||
** | by the Free Software Foundation, either version 3 of the License, or (at |
|
||||
** | your option) any later version. |
|
||||
** | |
|
||||
** | The Noja Interpreter is distributed in the hope that it will be useful, |
|
||||
** | but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||||
** | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General |
|
||||
** | Public License for more details. |
|
||||
** | |
|
||||
** | You should have received a copy of the GNU General Public License along |
|
||||
** | with The Noja Interpreter. If not, see <http://www.gnu.org/licenses/>. |
|
||||
** +--------------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include "hash.h"
|
||||
|
||||
int hashbytes(unsigned char *str, int len)
|
||||
{
|
||||
int x = 0; // Temp?
|
||||
|
||||
x ^= *str << 7;
|
||||
|
||||
for(int i = 0; i < len; i += 1)
|
||||
x = (1000003UL * x) ^ *str++;
|
||||
|
||||
x ^= len;
|
||||
|
||||
if(x == -1)
|
||||
x = -2;
|
||||
|
||||
return x;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
|
||||
/* +--------------------------------------------------------------------------+
|
||||
** | _ _ _ |
|
||||
** | | \ | | (_) |
|
||||
** | | \| | ___ _ __ _ |
|
||||
** | | . ` |/ _ \| |/ _` | |
|
||||
** | | |\ | (_) | | (_| | |
|
||||
** | |_| \_|\___/| |\__,_| |
|
||||
** | _/ | |
|
||||
** | |__/ |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | Copyright (c) 2022 Francesco Cozzuto <francesco.cozzuto@gmail.com> |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | This file is part of The Noja Interpreter. |
|
||||
** | |
|
||||
** | The Noja Interpreter is free software: you can redistribute it and/or |
|
||||
** | modify it under the terms of the GNU General Public License as published |
|
||||
** | by the Free Software Foundation, either version 3 of the License, or (at |
|
||||
** | your option) any later version. |
|
||||
** | |
|
||||
** | The Noja Interpreter is distributed in the hope that it will be useful, |
|
||||
** | but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||||
** | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General |
|
||||
** | Public License for more details. |
|
||||
** | |
|
||||
** | You should have received a copy of the GNU General Public License along |
|
||||
** | with The Noja Interpreter. If not, see <http://www.gnu.org/licenses/>. |
|
||||
** +--------------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#ifndef HASH_H
|
||||
#define HASH_H
|
||||
int hashbytes(unsigned char *str, int len);
|
||||
#endif
|
||||
@@ -0,0 +1,99 @@
|
||||
#include <string.h>
|
||||
#include "labellist.h"
|
||||
|
||||
typedef struct LabelInfo LabelInfo;
|
||||
struct LabelInfo {
|
||||
const char *name;
|
||||
size_t name_len;
|
||||
Promise *promise;
|
||||
LabelInfo *next;
|
||||
};
|
||||
|
||||
struct LabelList {
|
||||
BPAlloc *alloc;
|
||||
LabelInfo *head;
|
||||
};
|
||||
|
||||
LabelList *LabelList_New(BPAlloc *alloc)
|
||||
{
|
||||
LabelList *list = BPAlloc_Malloc(alloc, sizeof(LabelList));
|
||||
if(list != NULL) {
|
||||
list->head = NULL;
|
||||
list->alloc = alloc;
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
void LabelList_Free(LabelList *list)
|
||||
{
|
||||
LabelInfo *info = list->head;
|
||||
while(info != NULL) {
|
||||
Promise_Free(info->promise);
|
||||
info = info->next;
|
||||
}
|
||||
list->head = NULL;
|
||||
}
|
||||
|
||||
bool LabelList_SetLabel(LabelList *list, const char *name, size_t name_len, long long int value)
|
||||
{
|
||||
Promise *promise = LabelList_GetLabel(list, name, name_len);
|
||||
if(promise == NULL)
|
||||
return false;
|
||||
Promise_Resolve(promise, &value, sizeof(value));
|
||||
return true;
|
||||
}
|
||||
|
||||
Promise *LabelList_GetLabel(LabelList *list, const char *name, size_t name_len)
|
||||
{
|
||||
// Find the label with the given name.
|
||||
{
|
||||
LabelInfo *info = list->head;
|
||||
while(info != NULL) {
|
||||
|
||||
if(name_len == info->name_len
|
||||
&& strncmp(name, info->name, name_len) == 0)
|
||||
return info->promise;
|
||||
|
||||
info = info->next;
|
||||
}
|
||||
}
|
||||
|
||||
// No such label. Create a new one.
|
||||
LabelInfo *new_info;
|
||||
{
|
||||
BPAlloc *alloc = list->alloc;
|
||||
Promise *promise = Promise_New(alloc, sizeof(long long int));
|
||||
if(promise == NULL)\
|
||||
return NULL;
|
||||
|
||||
new_info = BPAlloc_Malloc(alloc, sizeof(LabelInfo));
|
||||
if(new_info == NULL) {
|
||||
Promise_Free(promise);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
new_info->next = NULL;
|
||||
new_info->name = name;
|
||||
new_info->name_len = name_len;
|
||||
new_info->promise = promise;
|
||||
}
|
||||
|
||||
// Add it to the list
|
||||
new_info->next = list->head;
|
||||
list->head = new_info;
|
||||
|
||||
return new_info->promise;
|
||||
}
|
||||
|
||||
size_t LabelList_GetUnresolvedCount(LabelList *list)
|
||||
{
|
||||
size_t count = 0;
|
||||
|
||||
LabelInfo *info = list->head;
|
||||
while(info != NULL) {
|
||||
count += !Promise_hasResolved(info->promise);
|
||||
info = info->next;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
#ifndef LABELLIST_H
|
||||
#define LABELLIST_H
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include "bpalloc.h"
|
||||
#include "promise.h"
|
||||
typedef struct LabelList LabelList;
|
||||
LabelList *LabelList_New(BPAlloc *alloc);
|
||||
void LabelList_Free(LabelList *list);
|
||||
bool LabelList_SetLabel(LabelList *list, const char *name, size_t name_len, long long int value);
|
||||
Promise *LabelList_GetLabel(LabelList *list, const char *name, size_t name_len);
|
||||
size_t LabelList_GetUnresolvedCount(LabelList *list);
|
||||
#endif /* LABELLIST_H */
|
||||
@@ -0,0 +1,142 @@
|
||||
|
||||
/* +--------------------------------------------------------------------------+
|
||||
** | _ _ _ |
|
||||
** | | \ | | (_) |
|
||||
** | | \| | ___ _ __ _ |
|
||||
** | | . ` |/ _ \| |/ _` | |
|
||||
** | | |\ | (_) | | (_| | |
|
||||
** | |_| \_|\___/| |\__,_| |
|
||||
** | _/ | |
|
||||
** | |__/ |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | Copyright (c) 2022 Francesco Cozzuto <francesco.cozzuto@gmail.com> |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | This file is part of The Noja Interpreter. |
|
||||
** | |
|
||||
** | The Noja Interpreter is free software: you can redistribute it and/or |
|
||||
** | modify it under the terms of the GNU General Public License as published |
|
||||
** | by the Free Software Foundation, either version 3 of the License, or (at |
|
||||
** | your option) any later version. |
|
||||
** | |
|
||||
** | The Noja Interpreter is distributed in the hope that it will be useful, |
|
||||
** | but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||||
** | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General |
|
||||
** | Public License for more details. |
|
||||
** | |
|
||||
** | You should have received a copy of the GNU General Public License along |
|
||||
** | with The Noja Interpreter. If not, see <http://www.gnu.org/licenses/>. |
|
||||
** +--------------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
_Bool Promise_hasResolved(Promise *promise)
|
||||
{
|
||||
return promise->set;
|
||||
}
|
||||
|
||||
void Promise_Free(Promise *promise)
|
||||
{
|
||||
(void) 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;
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
|
||||
/* +--------------------------------------------------------------------------+
|
||||
** | _ _ _ |
|
||||
** | | \ | | (_) |
|
||||
** | | \| | ___ _ __ _ |
|
||||
** | | . ` |/ _ \| |/ _` | |
|
||||
** | | |\ | (_) | | (_| | |
|
||||
** | |_| \_|\___/| |\__,_| |
|
||||
** | _/ | |
|
||||
** | |__/ |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | Copyright (c) 2022 Francesco Cozzuto <francesco.cozzuto@gmail.com> |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | This file is part of The Noja Interpreter. |
|
||||
** | |
|
||||
** | The Noja Interpreter is free software: you can redistribute it and/or |
|
||||
** | modify it under the terms of the GNU General Public License as published |
|
||||
** | by the Free Software Foundation, either version 3 of the License, or (at |
|
||||
** | your option) any later version. |
|
||||
** | |
|
||||
** | The Noja Interpreter is distributed in the hope that it will be useful, |
|
||||
** | but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||||
** | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General |
|
||||
** | Public License for more details. |
|
||||
** | |
|
||||
** | You should have received a copy of the GNU General Public License along |
|
||||
** | with The Noja Interpreter. If not, see <http://www.gnu.org/licenses/>. |
|
||||
** +--------------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#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_Free(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*));
|
||||
_Bool Promise_hasResolved(Promise *promise);
|
||||
#endif
|
||||
@@ -0,0 +1,193 @@
|
||||
|
||||
/* +--------------------------------------------------------------------------+
|
||||
** | _ _ _ |
|
||||
** | | \ | | (_) |
|
||||
** | | \| | ___ _ __ _ |
|
||||
** | | . ` |/ _ \| |/ _` | |
|
||||
** | | |\ | (_) | | (_| | |
|
||||
** | |_| \_|\___/| |\__,_| |
|
||||
** | _/ | |
|
||||
** | |__/ |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | Copyright (c) 2022 Francesco Cozzuto <francesco.cozzuto@gmail.com> |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | This file is part of The Noja Interpreter. |
|
||||
** | |
|
||||
** | The Noja Interpreter is free software: you can redistribute it and/or |
|
||||
** | modify it under the terms of the GNU General Public License as published |
|
||||
** | by the Free Software Foundation, either version 3 of the License, or (at |
|
||||
** | your option) any later version. |
|
||||
** | |
|
||||
** | The Noja Interpreter is distributed in the hope that it will be useful, |
|
||||
** | but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||||
** | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General |
|
||||
** | Public License for more details. |
|
||||
** | |
|
||||
** | You should have received a copy of the GNU General Public License along |
|
||||
** | with The Noja Interpreter. If not, see <http://www.gnu.org/licenses/>. |
|
||||
** +--------------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#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_SET))
|
||||
{
|
||||
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);
|
||||
else
|
||||
s->name = NULL;
|
||||
|
||||
strncpy(s->body, body, size);
|
||||
return s;
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
|
||||
/* +--------------------------------------------------------------------------+
|
||||
** | _ _ _ |
|
||||
** | | \ | | (_) |
|
||||
** | | \| | ___ _ __ _ |
|
||||
** | | . ` |/ _ \| |/ _` | |
|
||||
** | | |\ | (_) | | (_| | |
|
||||
** | |_| \_|\___/| |\__,_| |
|
||||
** | _/ | |
|
||||
** | |__/ |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | Copyright (c) 2022 Francesco Cozzuto <francesco.cozzuto@gmail.com> |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | This file is part of The Noja Interpreter. |
|
||||
** | |
|
||||
** | The Noja Interpreter is free software: you can redistribute it and/or |
|
||||
** | modify it under the terms of the GNU General Public License as published |
|
||||
** | by the Free Software Foundation, either version 3 of the License, or (at |
|
||||
** | your option) any later version. |
|
||||
** | |
|
||||
** | The Noja Interpreter is distributed in the hope that it will be useful, |
|
||||
** | but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||||
** | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General |
|
||||
** | Public License for more details. |
|
||||
** | |
|
||||
** | You should have received a copy of the GNU General Public License along |
|
||||
** | with The Noja Interpreter. If not, see <http://www.gnu.org/licenses/>. |
|
||||
** +--------------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#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
|
||||
@@ -0,0 +1,182 @@
|
||||
|
||||
/* +--------------------------------------------------------------------------+
|
||||
** | _ _ _ |
|
||||
** | | \ | | (_) |
|
||||
** | | \| | ___ _ __ _ |
|
||||
** | | . ` |/ _ \| |/ _` | |
|
||||
** | | |\ | (_) | | (_| | |
|
||||
** | |_| \_|\___/| |\__,_| |
|
||||
** | _/ | |
|
||||
** | |__/ |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | Copyright (c) 2022 Francesco Cozzuto <francesco.cozzuto@gmail.com> |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | This file is part of The Noja Interpreter. |
|
||||
** | |
|
||||
** | The Noja Interpreter is free software: you can redistribute it and/or |
|
||||
** | modify it under the terms of the GNU General Public License as published |
|
||||
** | by the Free Software Foundation, either version 3 of the License, or (at |
|
||||
** | your option) any later version. |
|
||||
** | |
|
||||
** | The Noja Interpreter is distributed in the hope that it will be useful, |
|
||||
** | but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||||
** | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General |
|
||||
** | Public License for more details. |
|
||||
** | |
|
||||
** | You should have received a copy of the GNU General Public License along |
|
||||
** | with The Noja Interpreter. If not, see <http://www.gnu.org/licenses/>. |
|
||||
** +--------------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include "stack.h"
|
||||
#include "defs.h"
|
||||
|
||||
struct xStack {
|
||||
unsigned int size,
|
||||
used;
|
||||
int refs;
|
||||
void *body[];
|
||||
};
|
||||
|
||||
_Bool Stack_IsReadOnlyCopy(Stack *s)
|
||||
{
|
||||
return (uintptr_t) s & (uintptr_t) 1;
|
||||
}
|
||||
|
||||
void *Stack_New(int size)
|
||||
{
|
||||
if(size < 0)
|
||||
size = 1024;
|
||||
|
||||
Stack *s = malloc(sizeof(Stack) + sizeof(void*) * size);
|
||||
|
||||
if(s == NULL)
|
||||
return NULL;
|
||||
|
||||
assert((intptr_t) s % 8 == 0);
|
||||
|
||||
s->size = size;
|
||||
s->used = 0;
|
||||
s->refs = 1;
|
||||
return s;
|
||||
}
|
||||
|
||||
static Stack *unmark(Stack *s)
|
||||
{
|
||||
return (Stack*) ((intptr_t) s & ~ (intptr_t) 1);
|
||||
}
|
||||
|
||||
void *Stack_Top(Stack *s, int n)
|
||||
{
|
||||
assert(n <= 0);
|
||||
|
||||
// Remove readonly bit.
|
||||
s = unmark(s);
|
||||
|
||||
if(s->used == 0)
|
||||
return NULL;
|
||||
|
||||
if((int) s->used + n - 1 < 0)
|
||||
return NULL;
|
||||
|
||||
return s->body[s->used + n - 1];
|
||||
}
|
||||
|
||||
void **Stack_TopRef(Stack *s, int n)
|
||||
{
|
||||
assert(n <= 0);
|
||||
|
||||
if(Stack_IsReadOnlyCopy(s))
|
||||
return NULL;
|
||||
|
||||
// Remove readonly bit.
|
||||
s = unmark(s);
|
||||
|
||||
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(Stack_IsReadOnlyCopy(s))
|
||||
return 0;
|
||||
|
||||
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(Stack_IsReadOnlyCopy(s))
|
||||
return 0;
|
||||
|
||||
if(s->used == s->size)
|
||||
return 0;
|
||||
|
||||
s->body[s->used] = item;
|
||||
s->used += 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
Stack *Stack_Copy(Stack *s, _Bool readonly)
|
||||
{
|
||||
if(Stack_IsReadOnlyCopy(s))
|
||||
{
|
||||
// Reference is readonly,
|
||||
// so the copy must be
|
||||
// readonly.
|
||||
readonly = 1;
|
||||
|
||||
// Remove readonly bit.
|
||||
s = unmark(s);
|
||||
}
|
||||
|
||||
s->refs += 1;
|
||||
|
||||
if(readonly)
|
||||
return (Stack*) ((uintptr_t) s | (intptr_t) 1);
|
||||
else
|
||||
return s;
|
||||
}
|
||||
|
||||
void Stack_Free(Stack *s)
|
||||
{
|
||||
// Remove readonly bit.
|
||||
s = unmark(s);
|
||||
|
||||
s->refs -= 1;
|
||||
assert(s->refs >= 0);
|
||||
|
||||
if(s->refs == 0)
|
||||
free(s);
|
||||
}
|
||||
|
||||
unsigned int Stack_Size(Stack *s)
|
||||
{
|
||||
// Remove readonly bit.
|
||||
s = unmark(s);
|
||||
|
||||
return s->used;
|
||||
}
|
||||
|
||||
unsigned int Stack_Capacity(Stack *s)
|
||||
{
|
||||
// Remove readonly bit.
|
||||
s = unmark(s);
|
||||
|
||||
return s->size;
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
|
||||
/* +--------------------------------------------------------------------------+
|
||||
** | _ _ _ |
|
||||
** | | \ | | (_) |
|
||||
** | | \| | ___ _ __ _ |
|
||||
** | | . ` |/ _ \| |/ _` | |
|
||||
** | | |\ | (_) | | (_| | |
|
||||
** | |_| \_|\___/| |\__,_| |
|
||||
** | _/ | |
|
||||
** | |__/ |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | Copyright (c) 2022 Francesco Cozzuto <francesco.cozzuto@gmail.com> |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | This file is part of The Noja Interpreter. |
|
||||
** | |
|
||||
** | The Noja Interpreter is free software: you can redistribute it and/or |
|
||||
** | modify it under the terms of the GNU General Public License as published |
|
||||
** | by the Free Software Foundation, either version 3 of the License, or (at |
|
||||
** | your option) any later version. |
|
||||
** | |
|
||||
** | The Noja Interpreter is distributed in the hope that it will be useful, |
|
||||
** | but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||||
** | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General |
|
||||
** | Public License for more details. |
|
||||
** | |
|
||||
** | You should have received a copy of the GNU General Public License along |
|
||||
** | with The Noja Interpreter. If not, see <http://www.gnu.org/licenses/>. |
|
||||
** +--------------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#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);
|
||||
Stack *Stack_Copy(Stack *s, _Bool readonly);
|
||||
void **Stack_TopRef(Stack *s, int n);
|
||||
unsigned int Stack_Capacity(Stack *s);
|
||||
_Bool Stack_IsReadOnlyCopy(Stack *s);
|
||||
#endif
|
||||
@@ -0,0 +1,453 @@
|
||||
|
||||
/* +--------------------------------------------------------------------------+
|
||||
** | _ _ _ |
|
||||
** | | \ | | (_) |
|
||||
** | | \| | ___ _ __ _ |
|
||||
** | | . ` |/ _ \| |/ _` | |
|
||||
** | | |\ | (_) | | (_| | |
|
||||
** | |_| \_|\___/| |\__,_| |
|
||||
** | _/ | |
|
||||
** | |__/ |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | Copyright (c) 2022 Francesco Cozzuto <francesco.cozzuto@gmail.com> |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | This file is part of The Noja Interpreter. |
|
||||
** | |
|
||||
** | The Noja Interpreter is free software: you can redistribute it and/or |
|
||||
** | modify it under the terms of the GNU General Public License as published |
|
||||
** | by the Free Software Foundation, either version 3 of the License, or (at |
|
||||
** | your option) any later version. |
|
||||
** | |
|
||||
** | The Noja Interpreter is distributed in the hope that it will be useful, |
|
||||
** | but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||||
** | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General |
|
||||
** | Public License for more details. |
|
||||
** | |
|
||||
** | You should have received a copy of the GNU General Public License along |
|
||||
** | with The Noja Interpreter. If not, see <http://www.gnu.org/licenses/>. |
|
||||
** +--------------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stddef.h> // NULL
|
||||
#include "utf8.h"
|
||||
|
||||
// If this is turned on, these functions will assume
|
||||
// the UTF-8 strings will mainly contain ASCII characters.
|
||||
#define ASSUME_ASCII 1
|
||||
|
||||
/* SYMBOL
|
||||
** utf8_sequence_from_utf32_codepoint
|
||||
**
|
||||
** DESCRIPTION
|
||||
** Transform a UTF-32 encoded codepoint to a UTF-8 encoded byte sequence.
|
||||
**
|
||||
** ARGUMENTS
|
||||
** The [utf8_data] pointer refers to the location where the UTF-8 sequence
|
||||
** will be stored.
|
||||
**
|
||||
** The [nbytes] argument specifies the maximum number of bytes that can
|
||||
** be written to [utf8_data]. It can't be negative.
|
||||
**
|
||||
** The [utf32_code] argument is the UTF-32 code that will be converted.
|
||||
**
|
||||
** RETURN
|
||||
** If [utf32_code] is valid UTF-32 and the provided buffer is big enough,
|
||||
** the UTF-8 equivalent sequence is stored in [utf8_data]. No more than
|
||||
** [nbytes] are ever written. If one of those conitions isn't true, -1 is
|
||||
** returned.
|
||||
*/
|
||||
int utf8_sequence_from_utf32_codepoint(char *utf8_data, int nbytes, uint32_t utf32_code)
|
||||
{
|
||||
if(utf32_code < 128)
|
||||
{
|
||||
if(nbytes < 1)
|
||||
return -1;
|
||||
|
||||
utf8_data[0] = utf32_code;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(utf32_code < 2048)
|
||||
{
|
||||
if(nbytes < 2)
|
||||
return -1;
|
||||
|
||||
utf8_data[0] = 0xc0 | (utf32_code >> 6);
|
||||
utf8_data[1] = 0x80 | (utf32_code & 0x3f);
|
||||
return 2;
|
||||
}
|
||||
|
||||
if(utf32_code < 65536)
|
||||
{
|
||||
if(nbytes < 3)
|
||||
return -1;
|
||||
|
||||
utf8_data[0] = 0xe0 | (utf32_code >> 12);
|
||||
utf8_data[1] = 0x80 | ((utf32_code >> 6) & 0x3f);
|
||||
utf8_data[2] = 0x80 | (utf32_code & 0x3f);
|
||||
return 3;
|
||||
}
|
||||
|
||||
if(utf32_code <= 0x10ffff)
|
||||
{
|
||||
if(nbytes < 4)
|
||||
return -1;
|
||||
|
||||
utf8_data[0] = 0xf0 | (utf32_code >> 18);
|
||||
utf8_data[1] = 0x80 | ((utf32_code >> 12) & 0x3f);
|
||||
utf8_data[2] = 0x80 | ((utf32_code >> 6) & 0x3f);
|
||||
utf8_data[3] = 0x80 | (utf32_code & 0x3f);
|
||||
return 4;
|
||||
}
|
||||
|
||||
// Code is out of range for UTF-8.
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* SYMBOL
|
||||
** utf8_sequence_to_utf32_codepoint
|
||||
**
|
||||
** DESCRIPTION
|
||||
** Transform a UTF-8 encoded byte sequence pointed by `utf8_data`
|
||||
** into a UTF-32 encoded codepoint.
|
||||
**
|
||||
** ARGUMENTS
|
||||
** The [utf8_data] pointer refers to the location of the UTF-8 sequence.
|
||||
**
|
||||
** The [nbytes] argument specifies the maximum number of bytes that can
|
||||
** be read after [utf8_data]. It can't be negative.
|
||||
**
|
||||
** NOTE: The [nbytes] argument has no relation to the UTF-8 byte count sequence.
|
||||
** You may think about this argument as the "raw" string length (the one
|
||||
** [strlen] whould return if [utf8_data] were zero-terminated).
|
||||
**
|
||||
** The [utf32_code] argument is the location where the encoded UTF-32 code
|
||||
** will be stored. It may be NULL, in which case the value is evaluated and then
|
||||
** thrown away.
|
||||
**
|
||||
** RETURN
|
||||
** The codepoint is returned through the output parameter `utf32_code`.
|
||||
** The returned value is the number of bytes of the UTF-8 sequence that
|
||||
** were scanned to encode the UTF-32 code, or -1 if the UTF-8 sequence
|
||||
** is invalid.
|
||||
**
|
||||
** NOTE: By calling this function with a NULL [utf32_code], you can check the
|
||||
** validity of a UTF-8 sequence.
|
||||
*/
|
||||
int utf8_sequence_to_utf32_codepoint(const char *utf8_data, int nbytes, uint32_t *utf32_code)
|
||||
{
|
||||
assert(utf8_data != NULL);
|
||||
assert(nbytes >= 0);
|
||||
|
||||
uint32_t dummy;
|
||||
if(utf32_code == NULL)
|
||||
utf32_code = &dummy;
|
||||
|
||||
if(nbytes == 0)
|
||||
return -1;
|
||||
|
||||
if(utf8_data[0] & 0x80)
|
||||
{
|
||||
// May be UTF-8.
|
||||
|
||||
if((unsigned char) utf8_data[0] >= 0xF0)
|
||||
{
|
||||
// 4 bytes.
|
||||
// 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
|
||||
|
||||
if(nbytes < 4)
|
||||
return -1;
|
||||
|
||||
uint32_t temp
|
||||
= (((uint32_t) utf8_data[0] & 0x07) << 18)
|
||||
| (((uint32_t) utf8_data[1] & 0x3f) << 12)
|
||||
| (((uint32_t) utf8_data[2] & 0x3f) << 6)
|
||||
| (((uint32_t) utf8_data[3] & 0x3f));
|
||||
|
||||
if(temp > 0x10ffff)
|
||||
return -1;
|
||||
|
||||
*utf32_code = temp;
|
||||
return 4;
|
||||
}
|
||||
|
||||
if((unsigned char) utf8_data[0] >= 0xE0)
|
||||
{
|
||||
// 3 bytes.
|
||||
// 1110xxxx 10xxxxxx 10xxxxxx
|
||||
|
||||
if(nbytes < 3)
|
||||
return -1;
|
||||
|
||||
uint32_t temp
|
||||
= (((uint32_t) utf8_data[0] & 0x0f) << 12)
|
||||
| (((uint32_t) utf8_data[1] & 0x3f) << 6)
|
||||
| (((uint32_t) utf8_data[2] & 0x3f));
|
||||
|
||||
if(temp > 0x10ffff)
|
||||
return -1;
|
||||
|
||||
*utf32_code = temp;
|
||||
return 3;
|
||||
}
|
||||
|
||||
if((unsigned char) utf8_data[0] >= 0xC0)
|
||||
{
|
||||
// 2 bytes.
|
||||
// 110xxxxx 10xxxxxx
|
||||
|
||||
if(nbytes < 2)
|
||||
return -1;
|
||||
|
||||
*utf32_code
|
||||
= (((uint32_t) utf8_data[0] & 0x1f) << 6)
|
||||
| (((uint32_t) utf8_data[1] & 0x3f));
|
||||
|
||||
assert(*utf32_code <= 0x10ffff);
|
||||
return 2;
|
||||
}
|
||||
|
||||
// 1 byte
|
||||
// 10xxxxxx
|
||||
*utf32_code = (uint32_t) utf8_data[0] & 0x3f;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// It's ASCII
|
||||
// 0xxxxxxx
|
||||
|
||||
*utf32_code = (uint32_t) utf8_data[0];
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* SYMBOL
|
||||
** utf8_strlen
|
||||
**
|
||||
** DESCRIPTION
|
||||
** Count the number of characters of a UTF-8 string.
|
||||
**
|
||||
** NOTE: By "character" we mean a valid UTF-8 sequence.
|
||||
**
|
||||
** ARGUMENTS
|
||||
** The [utf8_data] pointer refers to the location of the UTF-8 string.
|
||||
**
|
||||
** The [nbytes] argument specifies the byte count of the string referred
|
||||
** by [utf8_data]. It can't be negative.
|
||||
**
|
||||
** RETURN
|
||||
** Returns the number of characters encoded by [utf8_data], or -1 if
|
||||
** the string is not valid UTF-8.
|
||||
**
|
||||
** NOTE: By calling this function on an ASCII-only string, the return
|
||||
** value is equal to [nbytes].
|
||||
**
|
||||
** NOTE: You can check the validity of a UTF-8 string
|
||||
** by calling this function and checking that it's
|
||||
** return value is not negative.
|
||||
*/
|
||||
int utf8_strlen(const char *utf8_data, int nbytes)
|
||||
{
|
||||
assert(utf8_data != NULL);
|
||||
assert(nbytes >= 0);
|
||||
|
||||
int len = 0;
|
||||
|
||||
int i = 0;
|
||||
while(i < nbytes)
|
||||
{
|
||||
|
||||
#if ASSUME_ASCII
|
||||
{
|
||||
int ASCII_start = i;
|
||||
|
||||
// Skip through ASCII
|
||||
while(i < nbytes && (utf8_data[i] & 0x80) == 0)
|
||||
i += 1;
|
||||
|
||||
int ASCII_end = i;
|
||||
|
||||
len += (ASCII_end - ASCII_start);
|
||||
|
||||
// Either we scanned through all of the
|
||||
// string, or we encountered some unicode.
|
||||
|
||||
if(i == nbytes)
|
||||
// String ended.
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Found unicode.
|
||||
{
|
||||
int n = utf8_sequence_to_utf32_codepoint(utf8_data + i, nbytes - i, NULL);
|
||||
|
||||
if(n < 1)
|
||||
return -1;
|
||||
|
||||
i += n;
|
||||
len += 1;
|
||||
}
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
/* SYMBOL
|
||||
** utf8_prev
|
||||
**
|
||||
** DESCRIPTION
|
||||
** Get the UTF-8 sequence that comes before a given byte index
|
||||
** inside a given string.
|
||||
**
|
||||
** NOTE: This is what you use when you want to iterate over a
|
||||
** UTF-8 string backwards.
|
||||
**
|
||||
** ARGUMENTS
|
||||
** The [utf8_data] pointer refers to the location of the UTF-8 string.
|
||||
**
|
||||
** The [nbytes] argument is the raw size of the [utf8_data] string.
|
||||
** It can't be negative.
|
||||
**
|
||||
** The [idx] argument is the index of the byte that follows the UTF-8
|
||||
** sequence to be decoded.
|
||||
**
|
||||
** The [utf32_code] argument, if not NULL, is used to return the UTF-32
|
||||
** version of the decoded UTF-8 sequence.
|
||||
**
|
||||
** RETURN
|
||||
** Returns the index of the first byte of the decoded UTF-8 sequence,
|
||||
** or -1 is the sequence wasn't valid UTF-8.
|
||||
**
|
||||
** NOTE: If the function didn't fail, by subtracting the returned value
|
||||
** from [idx], you'll get the number of bytes of the decoded
|
||||
** sequence.
|
||||
*/
|
||||
int utf8_prev(const char *utf8_data, int nbytes, int idx, uint32_t *utf32_code)
|
||||
{
|
||||
assert(idx >= 0);
|
||||
assert(idx <= nbytes);
|
||||
|
||||
// [idx] currently refers to the head byte
|
||||
// of a UTF-8 sequence. We need to first
|
||||
// get to the last byte of the previous
|
||||
// sequence.
|
||||
idx -= 1;
|
||||
|
||||
if(idx == -1)
|
||||
// There was no previous sequence!
|
||||
return 0; // Return the same index that was provided.
|
||||
|
||||
int tail = idx;
|
||||
|
||||
#if ASSUME_ASCII
|
||||
{
|
||||
// This block isn't necessary for
|
||||
// this function to work but it
|
||||
// makes strings that are mainly ascii
|
||||
// to go faster.
|
||||
|
||||
if((utf8_data[tail] & 0x80) == 0)
|
||||
{
|
||||
if(utf32_code)
|
||||
*utf32_code = utf8_data[tail];
|
||||
return tail;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Skip all of the auxiliary bytes in the
|
||||
// form '10xxxxxx'.
|
||||
|
||||
while(idx > -1 && (utf8_data[idx] & 0xc0) == 0x80)
|
||||
idx -= 1;
|
||||
|
||||
if(idx == -1)
|
||||
{
|
||||
// No head sequence byte was found,
|
||||
// so this isn't valid UTF-8.
|
||||
return -1;
|
||||
}
|
||||
|
||||
// The index of the head byte.
|
||||
int head = idx;
|
||||
|
||||
// The number of auxiliary bytes is given
|
||||
// by the difference
|
||||
int aux = tail - head;
|
||||
|
||||
// The total number of bytes of the
|
||||
// sequence is [aux + 1].
|
||||
|
||||
int n = utf8_sequence_to_utf32_codepoint(utf8_data + head, aux + 1, utf32_code);
|
||||
|
||||
if(n < 1)
|
||||
// The sequence wasn't valid UTF-8.
|
||||
return -1;
|
||||
|
||||
assert(n > 0);
|
||||
|
||||
if(n < aux + 1)
|
||||
// Not all of the auxiliary bytes were considered while parsing.
|
||||
return -1;
|
||||
|
||||
assert(n == aux + 1);
|
||||
|
||||
return head;
|
||||
}
|
||||
|
||||
/* SYMBOL
|
||||
** utf8_next
|
||||
**
|
||||
** DESCRIPTION
|
||||
** Get the UTF-8 sequence from a UTF-8 string that starts AFTER the
|
||||
** sequence that starts at a given byte index.
|
||||
**
|
||||
** NOTE: This is what you use when you want to iterate over a
|
||||
** UTF-8 string.
|
||||
**
|
||||
** ARGUMENTS
|
||||
** The [utf8_data] pointer refers to the location of the UTF-8 string.
|
||||
**
|
||||
** The [nbytes] argument is the raw size of the [utf8_data] string.
|
||||
** It can't be negative.
|
||||
**
|
||||
** The [idx] argument is the index of the first byte of the sequence
|
||||
** that comes before the sequence to be decoded.
|
||||
**
|
||||
** The [utf32_code] argument, if not NULL, is used to return the UTF-32
|
||||
** version of the decoded UTF-8 sequence.
|
||||
**
|
||||
** RETURN
|
||||
** Returns the index of the first byte of the decoded UTF-8 sequence,
|
||||
** or -1 is the sequence wasn't valid UTF-8.
|
||||
**
|
||||
** NOTE: If the function didn't fail, by subtracting [idx] from the
|
||||
** returned value, you'll get the number of bytes of the decoded
|
||||
** sequence.
|
||||
*/
|
||||
int utf8_next(const char *utf8_data, int nbytes, int idx, uint32_t *utf32_code)
|
||||
{
|
||||
// Get the byte count of the current sequence.
|
||||
int n = utf8_sequence_to_utf32_codepoint(utf8_data + idx, nbytes, NULL);
|
||||
|
||||
if(n < 1)
|
||||
return -1;
|
||||
|
||||
// Now get the codepoint of the next sequence.
|
||||
int k = utf8_sequence_to_utf32_codepoint(utf8_data + idx + n, nbytes, utf32_code);
|
||||
|
||||
if(k < 1)
|
||||
return -1;
|
||||
|
||||
return idx + n;
|
||||
}
|
||||
|
||||
int utf8_curr(const char *utf8_data, int nbytes, int idx, uint32_t *utf32_code)
|
||||
{
|
||||
assert(idx >= 0);
|
||||
assert(idx < nbytes);
|
||||
|
||||
int n = utf8_sequence_to_utf32_codepoint(utf8_data + idx, nbytes - idx, utf32_code);
|
||||
|
||||
return n > 0;
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
|
||||
/* +--------------------------------------------------------------------------+
|
||||
** | _ _ _ |
|
||||
** | | \ | | (_) |
|
||||
** | | \| | ___ _ __ _ |
|
||||
** | | . ` |/ _ \| |/ _` | |
|
||||
** | | |\ | (_) | | (_| | |
|
||||
** | |_| \_|\___/| |\__,_| |
|
||||
** | _/ | |
|
||||
** | |__/ |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | Copyright (c) 2022 Francesco Cozzuto <francesco.cozzuto@gmail.com> |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | This file is part of The Noja Interpreter. |
|
||||
** | |
|
||||
** | The Noja Interpreter is free software: you can redistribute it and/or |
|
||||
** | modify it under the terms of the GNU General Public License as published |
|
||||
** | by the Free Software Foundation, either version 3 of the License, or (at |
|
||||
** | your option) any later version. |
|
||||
** | |
|
||||
** | The Noja Interpreter is distributed in the hope that it will be useful, |
|
||||
** | but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||||
** | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General |
|
||||
** | Public License for more details. |
|
||||
** | |
|
||||
** | You should have received a copy of the GNU General Public License along |
|
||||
** | with The Noja Interpreter. If not, see <http://www.gnu.org/licenses/>. |
|
||||
** +--------------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#ifndef UTF8_H
|
||||
#define UTF8_H
|
||||
#include <stdint.h> // uint32_t
|
||||
int utf8_sequence_from_utf32_codepoint(char *utf8_data, int nbytes, uint32_t utf32_code);
|
||||
int utf8_sequence_to_utf32_codepoint(const char *utf8_data, int nbytes, uint32_t *utf32_code);
|
||||
int utf8_strlen(const char *utf8_data, int nbytes);
|
||||
int utf8_prev(const char *utf8_data, int nbytes, int idx, uint32_t *utf32_code);
|
||||
int utf8_next(const char *utf8_data, int nbytes, int idx, uint32_t *utf32_code);
|
||||
int utf8_curr(const char *utf8_data, int nbytes, int idx, uint32_t *utf32_code);
|
||||
#endif // #ifndef UTF8_H
|
||||
Reference in New Issue
Block a user