bubblesort example in bytecode
This commit is contained in:
+2
-1
@@ -1,4 +1,5 @@
|
|||||||
cache
|
cache
|
||||||
vgcore.*
|
vgcore.*
|
||||||
libnoja.a
|
libnoja.a
|
||||||
noja
|
noja
|
||||||
|
*.gcov
|
||||||
+10
-11
@@ -1,13 +1,12 @@
|
|||||||
FILES="tests/assembler_test.c \
|
FILES="tests/assembler_test.c \
|
||||||
src/assembler/assembler.c \
|
src/noja/assembler/assembler.c \
|
||||||
src/common/executable.c \
|
src/noja/common/executable.c \
|
||||||
src/utils/error.c \
|
src/noja/utils/error.c \
|
||||||
src/utils/source.c \
|
src/noja/utils/source.c \
|
||||||
src/utils/bpalloc.c \
|
src/noja/utils/bpalloc.c \
|
||||||
src/utils/promise.c \
|
src/noja/utils/promise.c \
|
||||||
src/utils/labellist.c \
|
src/noja/utils/labellist.c \
|
||||||
src/utils/bucketlist.c"
|
src/noja/utils/bucketlist.c"
|
||||||
|
|
||||||
gcc $FILES -o build/assembler_test_cov -Wall -Wextra -g --coverage
|
|
||||||
gcc $FILES -o build/assembler_test -Wall -Wextra -g
|
|
||||||
|
|
||||||
|
gcc $FILES -o assembler_test_cov -Wall -Wextra -g --coverage
|
||||||
|
gcc $FILES -o assembler_test -Wall -Wextra -g
|
||||||
|
|||||||
@@ -0,0 +1,171 @@
|
|||||||
|
PUSHFUN copy, 1;
|
||||||
|
ASS "copy";
|
||||||
|
POP 1;
|
||||||
|
JUMP copy_end;
|
||||||
|
|
||||||
|
copy:
|
||||||
|
ASS "L";
|
||||||
|
POP 1;
|
||||||
|
PUSHLST 0;
|
||||||
|
ASS "L2";
|
||||||
|
POP 1;
|
||||||
|
PUSHINT 0;
|
||||||
|
ASS "i";
|
||||||
|
POP 1;
|
||||||
|
PUSHVAR "i";
|
||||||
|
PUSHVAR "L";
|
||||||
|
PUSHVAR "count";
|
||||||
|
CALL 1, 1;
|
||||||
|
LSS;
|
||||||
|
JUMPIFNOTANDPOP 31;
|
||||||
|
PUSHVAR "L";
|
||||||
|
PUSHVAR "i";
|
||||||
|
SELECT;
|
||||||
|
PUSHVAR "L2";
|
||||||
|
PUSHVAR "i";
|
||||||
|
INSERT2;
|
||||||
|
POP 1;
|
||||||
|
PUSHVAR "i";
|
||||||
|
PUSHINT 1;
|
||||||
|
ADD;
|
||||||
|
ASS "i";
|
||||||
|
POP 1;
|
||||||
|
JUMP 12;
|
||||||
|
PUSHVAR "L2";
|
||||||
|
RETURN 1;
|
||||||
|
RETURN 0;
|
||||||
|
|
||||||
|
copy_end:
|
||||||
|
PUSHFUN bubble_sort, 2;
|
||||||
|
ASS "bubble_sort";
|
||||||
|
POP 1;
|
||||||
|
JUMP bubble_sort_end;
|
||||||
|
|
||||||
|
bubble_sort:
|
||||||
|
ASS "less";
|
||||||
|
POP 1;
|
||||||
|
ASS "L";
|
||||||
|
POP 1;
|
||||||
|
PUSHVAR "less";
|
||||||
|
PUSHNNE;
|
||||||
|
EQL;
|
||||||
|
JUMPIFNOTANDPOP default_less_cb_end;
|
||||||
|
PUSHFUN 50, 2;
|
||||||
|
ASS "less";
|
||||||
|
POP 1;
|
||||||
|
JUMP default_less_cb_end;
|
||||||
|
|
||||||
|
default_less_cb:
|
||||||
|
ASS "b";
|
||||||
|
POP 1;
|
||||||
|
ASS "a";
|
||||||
|
POP 1;
|
||||||
|
PUSHVAR "a";
|
||||||
|
PUSHVAR "b";
|
||||||
|
LSS;
|
||||||
|
RETURN 1;
|
||||||
|
RETURN 0;
|
||||||
|
|
||||||
|
default_less_cb_end:
|
||||||
|
PUSHVAR "L";
|
||||||
|
PUSHVAR "copy";
|
||||||
|
CALL 1, 1;
|
||||||
|
ASS "L";
|
||||||
|
POP 1;
|
||||||
|
|
||||||
|
outer_loop_start:
|
||||||
|
PUSHFLS;
|
||||||
|
ASS "swapped";
|
||||||
|
POP 1;
|
||||||
|
PUSHINT 0;
|
||||||
|
ASS "i";
|
||||||
|
POP 1;
|
||||||
|
|
||||||
|
inner_loop_start:
|
||||||
|
PUSHVAR "i";
|
||||||
|
PUSHVAR "L";
|
||||||
|
PUSHVAR "count";
|
||||||
|
CALL 1, 1;
|
||||||
|
PUSHINT 1;
|
||||||
|
SUB;
|
||||||
|
LSS;
|
||||||
|
PUSHVAR "swapped";
|
||||||
|
NOT;
|
||||||
|
AND;
|
||||||
|
JUMPIFNOTANDPOP outer_loop_end;
|
||||||
|
PUSHVAR "L";
|
||||||
|
PUSHVAR "i";
|
||||||
|
SELECT;
|
||||||
|
PUSHVAR "L";
|
||||||
|
PUSHVAR "i";
|
||||||
|
PUSHINT 1;
|
||||||
|
ADD;
|
||||||
|
SELECT;
|
||||||
|
PUSHVAR "less";
|
||||||
|
CALL 2, 1;
|
||||||
|
JUMPIFNOTANDPOP if_branch_end;
|
||||||
|
PUSHTRU;
|
||||||
|
ASS "swapped";
|
||||||
|
POP 1;
|
||||||
|
PUSHVAR "L";
|
||||||
|
PUSHVAR "i";
|
||||||
|
PUSHINT 1;
|
||||||
|
ADD;
|
||||||
|
SELECT;
|
||||||
|
ASS "tmp";
|
||||||
|
POP 1;
|
||||||
|
PUSHVAR "L";
|
||||||
|
PUSHVAR "i";
|
||||||
|
SELECT;
|
||||||
|
PUSHVAR "L";
|
||||||
|
PUSHVAR "i";
|
||||||
|
PUSHINT 1;
|
||||||
|
ADD;
|
||||||
|
INSERT2;
|
||||||
|
POP 1;
|
||||||
|
PUSHVAR "tmp";
|
||||||
|
PUSHVAR "L";
|
||||||
|
PUSHVAR "i";
|
||||||
|
INSERT2;
|
||||||
|
POP 1;
|
||||||
|
|
||||||
|
if_branch_end:
|
||||||
|
PUSHVAR "i";
|
||||||
|
PUSHINT 1;
|
||||||
|
ADD;
|
||||||
|
ASS "i";
|
||||||
|
POP 1;
|
||||||
|
JUMP inner_loop_start;
|
||||||
|
|
||||||
|
outer_loop_end:
|
||||||
|
PUSHVAR "swapped";
|
||||||
|
JUMPIFANDPOP outer_loop_start;
|
||||||
|
PUSHVAR "L";
|
||||||
|
RETURN 1;
|
||||||
|
RETURN 0;
|
||||||
|
|
||||||
|
bubble_sort_end:
|
||||||
|
PUSHLST 5;
|
||||||
|
PUSHINT 0;
|
||||||
|
PUSHINT 3;
|
||||||
|
INSERT;
|
||||||
|
PUSHINT 1;
|
||||||
|
PUSHINT 2;
|
||||||
|
INSERT;
|
||||||
|
PUSHINT 2;
|
||||||
|
PUSHINT 1;
|
||||||
|
INSERT;
|
||||||
|
PUSHINT 3;
|
||||||
|
PUSHINT 6;
|
||||||
|
INSERT;
|
||||||
|
PUSHINT 4;
|
||||||
|
PUSHINT 0;
|
||||||
|
PUSHINT 2;
|
||||||
|
SUB;
|
||||||
|
INSERT;
|
||||||
|
PUSHVAR "bubble_sort";
|
||||||
|
CALL 1, 1;
|
||||||
|
PUSHVAR "print";
|
||||||
|
CALL 1, 1;
|
||||||
|
POP 1;
|
||||||
|
RETURN 0;
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
PUSHSTR "Hello, world!\n";
|
||||||
|
PUSHVAR "print";
|
||||||
|
CALL 1, 1;
|
||||||
|
POP 1;
|
||||||
|
RETURN 0;
|
||||||
@@ -57,8 +57,8 @@ LFLAGS_DEBUG =
|
|||||||
CFLAGS_RELEASE = -DNDEBUG -O3
|
CFLAGS_RELEASE = -DNDEBUG -O3
|
||||||
LFLAGS_RELEASE =
|
LFLAGS_RELEASE =
|
||||||
|
|
||||||
CFLAGS_COVERAGE_GCC =
|
CFLAGS_COVERAGE_GCC = -fprofile-arcs -ftest-coverage
|
||||||
LFLAGS_COVERAGE_GCC = --coverage
|
LFLAGS_COVERAGE_GCC = -lgcov
|
||||||
CFLAGS_COVERAGE_CLANG = -fprofile-instr-generate -fcoverage-mapping
|
CFLAGS_COVERAGE_CLANG = -fprofile-instr-generate -fcoverage-mapping
|
||||||
LFLAGS_COVERAGE_CLANG =
|
LFLAGS_COVERAGE_CLANG =
|
||||||
# NOTE: To support BUILD_MODE=COVERAGE for a new compiler,
|
# NOTE: To support BUILD_MODE=COVERAGE for a new compiler,
|
||||||
@@ -135,4 +135,4 @@ install: $(LIB) $(CLI)
|
|||||||
clean:
|
clean:
|
||||||
rm -rf $(OBJDIR)
|
rm -rf $(OBJDIR)
|
||||||
rm -f $(LIB)
|
rm -f $(LIB)
|
||||||
rm -f $(CLI)
|
rm -f $(CLI)
|
||||||
|
|||||||
+28
-2
@@ -39,7 +39,9 @@ static const char usage[] =
|
|||||||
" $ noja run file.noja\n"
|
" $ noja run file.noja\n"
|
||||||
" $ noja run inline \"print('some noja code');\"\n"
|
" $ noja run inline \"print('some noja code');\"\n"
|
||||||
" $ noja dis file.noja\n"
|
" $ noja dis file.noja\n"
|
||||||
" $ noja dis inline \"print('some noja code');\"\n";
|
" $ noja dis inline \"print('some noja code');\"\n"
|
||||||
|
" $ noja asm file.noja\n"
|
||||||
|
" $ noja asm inline \"PUSHINT 5; PUSHFLT 1.0; ADD; POP 1;\"\n";
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
@@ -76,6 +78,30 @@ int main(int argc, char **argv)
|
|||||||
r = NOJA_runFile(argv[2]);
|
r = NOJA_runFile(argv[2]);
|
||||||
return r ? 0 : -1;
|
return r ? 0 : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(!strcmp(argv[1], "asm"))
|
||||||
|
{
|
||||||
|
if(argc == 2)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Error: Missing source file.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
_Bool r;
|
||||||
|
|
||||||
|
if(!strcmp(argv[2], "inline"))
|
||||||
|
{
|
||||||
|
if(argc == 3)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Error: Missing source string.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
r = NOJA_runAssemblyString(argv[3]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
r = NOJA_runAssemblyFile(argv[2]);
|
||||||
|
return r ? 0 : -1;
|
||||||
|
}
|
||||||
|
|
||||||
if(!strcmp(argv[1], "dis"))
|
if(!strcmp(argv[1], "dis"))
|
||||||
{
|
{
|
||||||
@@ -111,4 +137,4 @@ int main(int argc, char **argv)
|
|||||||
fprintf(stderr, "Error: Incorrect usage.\n\n");
|
fprintf(stderr, "Error: Incorrect usage.\n\n");
|
||||||
fprintf(stderr, usage);
|
fprintf(stderr, usage);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,389 +0,0 @@
|
|||||||
#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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Skip the ending double quotes.
|
|
||||||
assert(ctx->cur < ctx->len && ctx->str[ctx->cur] == '"');
|
|
||||||
ctx->cur += 1;
|
|
||||||
|
|
||||||
size_t literal_length = ctx->cur - literal_offset;
|
|
||||||
|
|
||||||
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]));
|
|
||||||
|
|
||||||
double q = 1;
|
|
||||||
do {
|
|
||||||
// Transform each digit into it's integer value.
|
|
||||||
int d = ctx->str[ctx->cur] - '0';
|
|
||||||
assert(d >= 0 && d <= 9);
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
#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 */
|
|
||||||
+88
-12
@@ -4,6 +4,7 @@
|
|||||||
#include "utils/source.h"
|
#include "utils/source.h"
|
||||||
#include "compiler/parse.h"
|
#include "compiler/parse.h"
|
||||||
#include "compiler/compile.h"
|
#include "compiler/compile.h"
|
||||||
|
#include "assembler/assemble.h"
|
||||||
#include "builtins/basic.h"
|
#include "builtins/basic.h"
|
||||||
#include "noja.h"
|
#include "noja.h"
|
||||||
|
|
||||||
@@ -79,13 +80,8 @@ static Executable *build(Source *src)
|
|||||||
return exe;
|
return exe;
|
||||||
}
|
}
|
||||||
|
|
||||||
static _Bool interpret(Source *src)
|
static _Bool interpret(Executable *exe)
|
||||||
{
|
{
|
||||||
Executable *exe = build(src);
|
|
||||||
|
|
||||||
if(exe == NULL)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
Runtime *runt = Runtime_New(-1, 1024*1024, NULL, NULL);
|
Runtime *runt = Runtime_New(-1, 1024*1024, NULL, NULL);
|
||||||
|
|
||||||
if(runt == NULL)
|
if(runt == NULL)
|
||||||
@@ -95,7 +91,6 @@ static _Bool interpret(Source *src)
|
|||||||
Error_Report(&error, 1, "Couldn't initialize runtime");
|
Error_Report(&error, 1, "Couldn't initialize runtime");
|
||||||
print_error(NULL, &error);
|
print_error(NULL, &error);
|
||||||
Error_Free(&error);
|
Error_Free(&error);
|
||||||
Executable_Free(exe);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,7 +110,6 @@ static _Bool interpret(Source *src)
|
|||||||
assert(error.base.occurred == 1);
|
assert(error.base.occurred == 1);
|
||||||
print_error(NULL, (Error*) &error);
|
print_error(NULL, (Error*) &error);
|
||||||
RuntimeError_Free(&error);
|
RuntimeError_Free(&error);
|
||||||
Executable_Free(exe);
|
|
||||||
Runtime_Free(runt);
|
Runtime_Free(runt);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -143,8 +137,6 @@ static _Bool interpret(Source *src)
|
|||||||
}
|
}
|
||||||
|
|
||||||
Runtime_Free(runt);
|
Runtime_Free(runt);
|
||||||
Executable_Free(exe);
|
|
||||||
|
|
||||||
return retc > -1;
|
return retc > -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -174,8 +166,14 @@ static _Bool interpret_file(const char *file)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
_Bool r = interpret(src);
|
Executable *exe = build(src);
|
||||||
|
|
||||||
|
if(exe == NULL)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
_Bool r = interpret(exe);
|
||||||
|
|
||||||
|
Executable_Free(exe);
|
||||||
Source_Free(src);
|
Source_Free(src);
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
@@ -195,12 +193,80 @@ static _Bool interpret_code(const char *code)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
_Bool r = interpret(src);
|
Executable *exe = build(src);
|
||||||
|
|
||||||
|
if(exe == NULL)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
_Bool r = interpret(exe);
|
||||||
|
|
||||||
|
Executable_Free(exe);
|
||||||
Source_Free(src);
|
Source_Free(src);
|
||||||
return r;
|
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)
|
static _Bool disassemble_file(const char *file)
|
||||||
{
|
{
|
||||||
Error error;
|
Error error;
|
||||||
@@ -261,4 +327,14 @@ _Bool NOJA_dumpFileBytecode(const char *file)
|
|||||||
_Bool NOJA_dumpStringBytecode(const char *str)
|
_Bool NOJA_dumpStringBytecode(const char *str)
|
||||||
{
|
{
|
||||||
return disassemble_code(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);
|
||||||
}
|
}
|
||||||
+3
-1
@@ -1,7 +1,9 @@
|
|||||||
#ifndef NOJA_H
|
#ifndef NOJA_H
|
||||||
#define NOJA_H
|
#define NOJA_H
|
||||||
|
_Bool NOJA_runFile (const char *file);
|
||||||
_Bool NOJA_runString(const char *str);
|
_Bool NOJA_runString(const char *str);
|
||||||
_Bool NOJA_runFile(const char *file);
|
_Bool NOJA_runAssemblyFile(const char *file);
|
||||||
|
_Bool NOJA_runAssemblyString(const char *str);
|
||||||
_Bool NOJA_dumpFileBytecode(const char *file);
|
_Bool NOJA_dumpFileBytecode(const char *file);
|
||||||
_Bool NOJA_dumpStringBytecode(const char *str);
|
_Bool NOJA_dumpStringBytecode(const char *str);
|
||||||
#endif /* NOJA_H */
|
#endif /* NOJA_H */
|
||||||
+46
-4
@@ -1,7 +1,7 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include "../src/utils/bpalloc.h"
|
#include "../src/noja/utils/bpalloc.h"
|
||||||
#include "../src/assembler/assembler.h"
|
#include "../src/noja/assembler/assembler.h"
|
||||||
|
|
||||||
static void stopTesting_(const char *file, int line)
|
static void stopTesting_(const char *file, int line)
|
||||||
{
|
{
|
||||||
@@ -243,6 +243,45 @@ int main()
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Instruction with the wrong number of arguments.
|
||||||
|
{
|
||||||
|
.line = __LINE__,
|
||||||
|
.text = "POS 1;",
|
||||||
|
.fail = true,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
.line = __LINE__,
|
||||||
|
.text = "PUSHINT 1;"
|
||||||
|
"PUSHINT 2;"
|
||||||
|
"ADD;",
|
||||||
|
.fail = false,
|
||||||
|
.expected = (StaticExecutable) {
|
||||||
|
.instr_count = 3,
|
||||||
|
.instr_list = (StaticInstruction[]) {
|
||||||
|
{
|
||||||
|
.opcode = OPCODE_PUSHINT,
|
||||||
|
.operands = (Operand[]) {
|
||||||
|
{ OPTP_INT, .as_int = 1, }
|
||||||
|
},
|
||||||
|
.operand_count = 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.opcode = OPCODE_PUSHINT,
|
||||||
|
.operands = (Operand[]) {
|
||||||
|
{ OPTP_INT, .as_int = 2, }
|
||||||
|
},
|
||||||
|
.operand_count = 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.opcode = OPCODE_ADD,
|
||||||
|
.operands = NULL,
|
||||||
|
.operand_count = 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
Error error;
|
Error error;
|
||||||
@@ -262,14 +301,17 @@ int main()
|
|||||||
stopTestingIf(src == NULL);
|
stopTestingIf(src == NULL);
|
||||||
|
|
||||||
Executable *got_exe = assemble(src, &error);
|
Executable *got_exe = assemble(src, &error);
|
||||||
|
|
||||||
if(tests[i].fail == true) {
|
if(tests[i].fail == true) {
|
||||||
testCase(tests[i].line, got_exe == NULL, "Managed to assemble an invalid source");
|
testCase(tests[i].line, got_exe == NULL, "Managed to assemble an invalid source");
|
||||||
} else {
|
} else {
|
||||||
Executable *exp_exe = buildExpectedExecutable(alloc, &tests[i].expected);
|
Executable *exp_exe = buildExpectedExecutable(alloc, &tests[i].expected);
|
||||||
|
|
||||||
|
if(got_exe == NULL)
|
||||||
|
fprintf(stderr, "Build error :: %s.\n", error.message);
|
||||||
|
|
||||||
testCase(tests[i].line, got_exe != NULL, "Failed to assemble a valid source");
|
testCase(tests[i].line, got_exe != NULL, "Failed to assemble a valid source");
|
||||||
/*
|
/*
|
||||||
fprintf(stderr, "== Generated ==\n");
|
fprintf(stderr, "== Generated ==\n");
|
||||||
Executable_Dump(got_exe);
|
Executable_Dump(got_exe);
|
||||||
fprintf(stderr, "\n");
|
fprintf(stderr, "\n");
|
||||||
|
|||||||
Reference in New Issue
Block a user