new testing infrastructure

This commit is contained in:
Francesco Cozzuto
2023-01-22 02:22:44 +01:00
parent 533cf01920
commit 1780826a97
145 changed files with 1432 additions and 938 deletions
Executable → Regular
+1 -1
View File
@@ -168,4 +168,4 @@ router->plug("/logout", {
}
});
error(serve("127.0.0.1", 8080, router));
error(serve("127.0.0.1", 8080, router));
+1 -1
View File
@@ -1,4 +1,4 @@
make -B BUILD_MODE=COVERAGE CC=gcc
./test tests/suite
./test tests/compiler/ tests/runtime/
make report
firefox report/index.html
+53 -11
View File
@@ -107,12 +107,57 @@ static bool parseStringOperand(Context *ctx, Error *error, BPAlloc *alloc, Opera
assert(ctx->cur < ctx->len && ctx->str[ctx->cur] == '"');
ctx->cur += 1;
size_t literal_offset = ctx->cur;
char buffer[1024];
size_t used = 0;
// 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;
do {
size_t offset = ctx->cur;
while (ctx->cur < ctx->len && ctx->str[ctx->cur] != '\\' && ctx->str[ctx->cur] != '"')
ctx->cur++;
size_t length = ctx->cur - offset;
if (used + length > sizeof(buffer)) {
*ctx->error_offset = ctx->cur;
Error_Report(error, ErrorType_INTERNAL, "Buffer is too short to hold string literal");
return false;
}
memcpy(buffer + used, ctx->str + offset, length);
used += length;
if (ctx->cur < ctx->len && ctx->str[ctx->cur] == '\\') {
ctx->cur++;
if (ctx->cur == ctx->len) {
*ctx->error_offset = ctx->cur;
Error_Report(error, ErrorType_SYNTAX, "End of source inside a string literal");
return false;
}
char c;
switch (ctx->str[ctx->cur]) {
case 'n': c = '\n'; break;
case 't': c = '\t'; break;
case 'r': c = '\r'; break;
case '"': c = '"'; break;
case '\\': c = '\\'; break;
default:
*ctx->error_offset = ctx->cur;
Error_Report(error, ErrorType_SYNTAX, "End of source inside a string literal");
return false;
}
if (used + 1 > sizeof(buffer)) {
*ctx->error_offset = ctx->cur;
Error_Report(error, ErrorType_INTERNAL, "Buffer is too short to hold string literal");
return false;
}
buffer[used++] = c;
ctx->cur++;
}
} while (ctx->cur < ctx->len && ctx->str[ctx->cur] != '"');
if(ctx->cur == ctx->len) {
*ctx->error_offset = ctx->cur;
@@ -120,21 +165,18 @@ static bool parseStringOperand(Context *ctx, Error *error, BPAlloc *alloc, Opera
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);
char *copy = BPAlloc_Malloc(alloc, used+1);
if(copy == NULL) {
*ctx->error_offset = ctx->cur;
Error_Report(error, ErrorType_INTERNAL, "No memory");
return false;
}
memcpy(copy, ctx->str + literal_offset, literal_length);
copy[literal_length] = '\0';
memcpy(copy, buffer, used);
copy[used] = '\0';
op->type = OPTP_STRING;
op->as_string = copy;
+2 -2
View File
@@ -115,7 +115,7 @@ static int bin_print(Runtime *runtime, Object **argv, unsigned int argc, Object
UNUSED(error);
for(int i = 0; i < (int) argc; i += 1)
Object_Print(argv[i], stdout);
Object_Print(argv[i], Runtime_GetOutputStream(runtime));
return 0;
}
@@ -178,7 +178,7 @@ static int bin_input(Runtime *runtime, Object **argv, unsigned int argc, Object
while(1)
{
char c = getc(stdin);
char c = getc(Runtime_GetInputStream(runtime));
if(c == '\n')
break;
+1 -1
View File
@@ -446,7 +446,7 @@ AST *parse(Source *src, BPAlloc *alloc, Error *error, int *error_offset)
#ifdef DEBUG
token_dest = fopen("tokens.txt", "wb");
#endif
AST *ast = BPAlloc_Malloc(alloc, sizeof(AST));
if(ast == NULL)
+27 -1
View File
@@ -82,6 +82,10 @@ struct xRuntime {
Heap *heap;
TimingTable *timing;
FILE *stdin;
FILE *stdout;
FILE *stderr;
FailedFrame failed_frame;
};
@@ -112,12 +116,15 @@ RuntimeConfig Runtime_GetDefaultConfigs()
.stack = 1024,
.callback = { .func = NULL, .data = NULL },
.time = false,
.stdin = stdin,
.stdout = stdout,
.stderr = stderr,
};
}
Stack *Runtime_GetStack(Runtime *runtime)
{
return Stack_Copy(runtime->stack, 1);
return runtime->stack;
}
Heap *Runtime_GetHeap(Runtime *runtime)
@@ -352,6 +359,10 @@ Runtime *Runtime_New(RuntimeConfig config)
runtime->builtins = NULL;
runtime->frame = NULL;
runtime->depth = 0;
runtime->stdin = config.stdin;
runtime->stderr = config.stderr;
runtime->stdout = config.stdout;
return runtime;
}
@@ -367,6 +378,21 @@ void Runtime_Free(Runtime *runtime)
free(runtime);
}
FILE *Runtime_GetErrorStream(Runtime *runtime)
{
return runtime->stderr;
}
FILE *Runtime_GetOutputStream(Runtime *runtime)
{
return runtime->stdout;
}
FILE *Runtime_GetInputStream(Runtime *runtime)
{
return runtime->stdin;
}
_Bool Runtime_Push(Runtime *runtime, Error *error, Object *obj)
{
ASSERT(runtime != NULL);
+6 -1
View File
@@ -51,12 +51,17 @@ typedef struct {
bool time;
size_t heap;
size_t stack;
FILE *stdin;
FILE *stderr;
FILE *stdout;
RuntimeCallback callback;
} RuntimeConfig;
Runtime* Runtime_New(RuntimeConfig config);
void Runtime_Free(Runtime *runtime);
FILE *Runtime_GetErrorStream(Runtime *runtime);
FILE *Runtime_GetInputStream(Runtime *runtime);
FILE *Runtime_GetOutputStream(Runtime *runtime);
bool Runtime_plugDefaultBuiltins(Runtime *runtime, Error *error);
bool Runtime_plugBuiltinsFromString(Runtime *runtime, const char *string, Error *error);
bool Runtime_plugBuiltinsFromFile(Runtime *runtime, const char *file, Error *error);
+2 -52
View File
@@ -40,11 +40,6 @@ struct xStack {
void *body[];
};
_Bool Stack_IsReadOnlyCopy(Stack *s)
{
return (uintptr_t) s & (uintptr_t) 1;
}
void *Stack_New(int size)
{
if(size < 0)
@@ -63,18 +58,10 @@ void *Stack_New(int size)
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;
@@ -88,12 +75,6 @@ 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;
@@ -106,9 +87,6 @@ void **Stack_TopRef(Stack *s, int n)
_Bool Stack_Pop(Stack *s, unsigned int n)
{
if(Stack_IsReadOnlyCopy(s))
return 0;
if(s->used < n)
return 0;
@@ -121,9 +99,6 @@ _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;
@@ -132,51 +107,26 @@ _Bool Stack_Push(Stack *s, void *item)
return 1;
}
Stack *Stack_Copy(Stack *s, _Bool readonly)
Stack *Stack_Copy(Stack *s)
{
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;
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;
}
+1 -2
View File
@@ -37,8 +37,7 @@ _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);
Stack *Stack_Copy(Stack *s);
void **Stack_TopRef(Stack *s, int n);
unsigned int Stack_Capacity(Stack *s);
_Bool Stack_IsReadOnlyCopy(Stack *s);
#endif
+14
View File
@@ -0,0 +1,14 @@
#include "error.h"
const char *getErrorString(ErrorID error)
{
switch (error) {
case ErrorID_VOID: return "No error";
case ErrorID_OUTOFMEMORY: return "Out of memory";
case ErrorID_DUPLFIELD: return "Duplicate field";
case ErrorID_BADSYNTAX_NOAT: return "Missing '@' character";
case ErrorID_BADSYNTAX_NONAME: return "Missing field name";
case ErrorID_BADSYNTAX_NOFIELD: return "Missing field";
}
return "???";
}
+14
View File
@@ -0,0 +1,14 @@
#ifndef TEST_ERROR_H
#define TEST_ERROR_H
typedef enum {
ErrorID_VOID,
ErrorID_OUTOFMEMORY,
ErrorID_DUPLFIELD,
ErrorID_BADSYNTAX_NONAME,
ErrorID_BADSYNTAX_NOAT,
ErrorID_BADSYNTAX_NOFIELD,
} ErrorID;
const char *getErrorString(ErrorID error);
#endif
BIN
View File
Binary file not shown.
+140
View File
@@ -0,0 +1,140 @@
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include "field.h"
static void freeFieldName(NojaTestField *field)
{
if (field->name != field->name_maybe)
free(field->name);
}
static void freeFieldBody(NojaTestField *field)
{
if (field->body != field->body_maybe)
free(field->body);
}
void freeField(NojaTestField *field)
{
freeFieldName(field);
freeFieldBody(field);
}
static ErrorID setFieldName(NojaTestField *field, const char *str, size_t len)
{
if (len < sizeof(field->name_maybe))
field->name = field->name_maybe;
else {
field->name = malloc(len+1);
if (field->name == NULL)
return ErrorID_OUTOFMEMORY;
}
memcpy(field->name, str, len);
field->name[len] = '\0';
return ErrorID_VOID;
}
static ErrorID setFieldBody(NojaTestField *field, const char *str, size_t len)
{
if (len < sizeof(field->body_maybe))
field->body = field->body_maybe;
else {
field->body = malloc(len+1);
if (field->body == NULL)
return ErrorID_OUTOFMEMORY;
}
memcpy(field->body, str, len);
field->body[len] = '\0';
return ErrorID_VOID;
}
static int isFieldNameCharacter(int c)
{
return isalpha(c) || c == '_';
}
static ErrorID parseFieldName(NojaTestScanner *scanner, NojaTestField *field)
{
size_t offset;
size_t length;
offset = scanner->cur;
consumeCharactersThat(scanner, isFieldNameCharacter);
length = scanner->cur - offset;
if (length == 0)
return ErrorID_BADSYNTAX_NONAME; // Missing field name token after '@'
ErrorID error = setFieldName(field, scanner->src + offset, length);
if (error != ErrorID_VOID) return error;
return ErrorID_VOID;
}
static bool parseFieldBody(NojaTestScanner *scanner, NojaTestField *field)
{
size_t offset;
size_t length;
consumeCharactersThat(scanner, isspace);
bool no_term;
char term;
if (ifFollowsConsumeCharacter(scanner, '[')) {
no_term = false;
term = ']';
} else if (ifFollowsConsumeCharacter(scanner, '{')) {
no_term = false;
term = '}';
} else
no_term = true;
offset = scanner->cur;
while (!noMoreCharacters(scanner) && !followsCharacter(scanner, '@') && (no_term || !followsCharacter(scanner, term)))
scanner->cur++;
length = scanner->cur - offset;
if (!no_term)
ifFollowsConsumeCharacter(scanner, term);
return setFieldBody(field, scanner->src + offset, length);
}
ErrorID findField(NojaTestScanner *scanner)
{
consumeCharactersThat(scanner, isspace);
bool at_follows = ifFollowsConsumeCharacter(scanner, '@');
if (!at_follows) {
if (noMoreCharacters(scanner))
return ErrorID_BADSYNTAX_NOFIELD;
return ErrorID_BADSYNTAX_NOAT;
}
return ErrorID_VOID;
}
// Returns:
// 0 if there are no more fields
// -1 on error
// 1 if a field was parsed
ErrorID parseField(NojaTestScanner *scanner, NojaTestField *field)
{
ErrorID error;
error = findField(scanner);
if (error != ErrorID_VOID)
return error;
error = parseFieldName(scanner, field);
if (error != ErrorID_VOID)
return error;
error = parseFieldBody(scanner, field);
if (error != ErrorID_VOID) {
freeFieldName(field);
return error;
}
return ErrorID_VOID;
}
+20
View File
@@ -0,0 +1,20 @@
#ifndef TEST_FIELD_H
#define TEST_FIELD_H
#include <stddef.h>
#include "error.h"
#include "scanner.h"
typedef struct {
char name_maybe[128];
char body_maybe[512];
char *name;
char *body;
size_t name_len;
size_t body_len;
} NojaTestField;
void freeField(NojaTestField *field);
ErrorID parseField(NojaTestScanner *scanner, NojaTestField *field);
#endif
BIN
View File
Binary file not shown.
+139 -496
View File
@@ -1,497 +1,140 @@
#include <stdbool.h>
#include <assert.h>
#include <string.h>
#include <dirent.h>
#include <stdio.h>
#include <ctype.h>
#include "../lib/executable.h"
#include "../lib/utils/source.h"
#include "../lib/compiler/compile.h"
#include "../lib/assembler/assemble.h"
//Regular text
#define BLK "\e[0;30m"
#define RED "\e[0;31m"
#define GRN "\e[0;32m"
#define YEL "\e[0;33m"
#define BLU "\e[0;34m"
#define MAG "\e[0;35m"
#define CYN "\e[0;36m"
#define WHT "\e[0;37m"
//Regular bold text
#define BBLK "\e[1;30m"
#define BRED "\e[1;31m"
#define BGRN "\e[1;32m"
#define BYEL "\e[1;33m"
#define BBLU "\e[1;34m"
#define BMAG "\e[1;35m"
#define BCYN "\e[1;36m"
#define BWHT "\e[1;37m"
//Regular underline text
#define UBLK "\e[4;30m"
#define URED "\e[4;31m"
#define UGRN "\e[4;32m"
#define UYEL "\e[4;33m"
#define UBLU "\e[4;34m"
#define UMAG "\e[4;35m"
#define UCYN "\e[4;36m"
#define UWHT "\e[4;37m"
//Regular background
#define BLKB "\e[40m"
#define REDB "\e[41m"
#define GRNB "\e[42m"
#define YELB "\e[43m"
#define BLUB "\e[44m"
#define MAGB "\e[45m"
#define CYNB "\e[46m"
#define WHTB "\e[47m"
//High intensty background
#define BLKHB "\e[0;100m"
#define REDHB "\e[0;101m"
#define GRNHB "\e[0;102m"
#define YELHB "\e[0;103m"
#define BLUHB "\e[0;104m"
#define MAGHB "\e[0;105m"
#define CYNHB "\e[0;106m"
#define WHTHB "\e[0;107m"
//High intensty text
#define HBLK "\e[0;90m"
#define HRED "\e[0;91m"
#define HGRN "\e[0;92m"
#define HYEL "\e[0;93m"
#define HBLU "\e[0;94m"
#define HMAG "\e[0;95m"
#define HCYN "\e[0;96m"
#define HWHT "\e[0;97m"
//Bold high intensity text
#define BHBLK "\e[1;90m"
#define BHRED "\e[1;91m"
#define BHGRN "\e[1;92m"
#define BHYEL "\e[1;93m"
#define BHBLU "\e[1;94m"
#define BHMAG "\e[1;95m"
#define BHCYN "\e[1;96m"
#define BHWHT "\e[1;97m"
//Reset
#define reset "\e[0m"
#define CRESET "\e[0m"
#define COLOR_RESET "\e[0m"
// NOTE: From https://gist.github.com/RabaDabaDoba/145049536f815903c79944599c6f952a
typedef enum {
TokenType_END,
TokenType_TEXT,
TokenType_DIRECTIVE_SOURCE,
TokenType_DIRECTIVE_BYTECODE,
} TokenType;
typedef struct {
TokenType type;
size_t offset;
size_t length;
} Token;
static bool startsWithSourceDirective(const char *str, size_t len)
{
return (((7 < len && !isalpha(str[7])) || 7 == len)
&& str[0] == '#' && str[1] == 's'
&& str[2] == 'o' && str[3] == 'u'
&& str[4] == 'r' && str[5] == 'c'
&& str[6] == 'e');
}
static bool startsWithBytecodeDirective(const char *str, size_t len)
{
return (((9 < len && !isalpha(str[9])) || 9 == len)
&& str[0] == '#' && str[1] == 'b'
&& str[2] == 'y' && str[3] == 't'
&& str[4] == 'e' && str[5] == 'c'
&& str[6] == 'o' && str[7] == 'd'
&& str[8] == 'e');
}
static bool startsWithDirective(const char *str, size_t len)
{
return startsWithSourceDirective(str, len)
|| startsWithBytecodeDirective(str, len);
}
static Token nextToken(const char *str, size_t len, size_t *cur_)
{
size_t cur = *cur_;
while(cur < len && isspace(str[cur]))
cur += 1;
Token token;
if(cur == len) {
token.type = TokenType_END;
token.offset = cur;
token.length = 0;
} else if(startsWithSourceDirective(str + cur, len - cur)) {
token.type = TokenType_DIRECTIVE_SOURCE;
token.offset = cur;
cur += strlen("#source");
token.length = cur - token.offset;
} else if(startsWithBytecodeDirective(str + cur, len - cur)) {
token.type = TokenType_DIRECTIVE_BYTECODE;
token.offset = cur;
cur += strlen("#bytecode");
token.length = cur - token.offset;
} else {
token.type = TokenType_TEXT;
token.offset = cur;
while(1) {
// Consume all characters until
// the next '#'.
while(cur < len && str[cur] != '#')
cur += 1;
if(cur == len)
break;
// If it's the start of a directive,
// stop the loop. If it was not, skip
// the '#'.
if(startsWithDirective(str + cur, len - cur))
break;
cur += 1; // Skip the '#'
}
token.length = cur - token.offset;
}
*cur_ = cur;
return token;
}
typedef struct {
Source *source;
Source *bytecode;
} TestCase;
static bool parseTestCaseSource(Source *source, TestCase *testcase)
{
const char *str = Source_GetBody(source);
size_t len = Source_GetSize(source);
assert(str != NULL);
bool no_source = true;
size_t source_offset;
size_t source_length;
bool no_bytecode = true;
size_t bytecode_offset;
size_t bytecode_length;
size_t cur = 0;
while(1) {
Token token = nextToken(str, len, &cur);
if(token.type == TokenType_END)
break;
if(token.type == TokenType_TEXT) {
fprintf(stderr, RED "Error" CRESET " :: Expected a directive at offset %ld\n", token.offset);
return false;
}
assert(token.type == TokenType_DIRECTIVE_SOURCE
|| token.type == TokenType_DIRECTIVE_BYTECODE);
Token directive = token;
size_t directive_body_offset;
size_t directive_body_length;
size_t backup = cur;
token = nextToken(str, len, &cur);
switch(token.type) {
case TokenType_END:
case TokenType_DIRECTIVE_SOURCE:
case TokenType_DIRECTIVE_BYTECODE:
// Right after the previous directive
// there's either the end of the file
// or another directive. We assume
// that the directive is followed by
// and empty string.
directive_body_offset = token.offset;
directive_body_length = 0;
// Put the cursor back so that this
// token is tokenized again at the
// start of the next iteration.
cur = backup;
break;
case TokenType_TEXT:
directive_body_offset = token.offset;
directive_body_length = token.length;
break;
}
if(directive.type == TokenType_DIRECTIVE_SOURCE) {
if(no_source == false) {
fprintf(stderr, RED "Error" CRESET " :: #source directive specified twice.\n");
return false;
}
no_source = false;
source_offset = directive_body_offset;
source_length = directive_body_length;
} else {
assert(directive.type == TokenType_DIRECTIVE_BYTECODE);
if(no_bytecode == false) {
fprintf(stderr, RED "Error" CRESET " :: #bytecode directive specified twice.\n");
return false;
}
no_bytecode = false;
bytecode_offset = directive_body_offset;
bytecode_length = directive_body_length;
}
}
if(no_source == true && no_bytecode == true) {
fprintf(stderr, RED "Error" CRESET " :: Missing both #source and #bytecode directives.\n");
return false;
}
if(no_source == true) {
fprintf(stderr, RED "Error" CRESET " :: Missing #source directive.\n");
return false;
}
if(no_bytecode == true) {
fprintf(stderr, RED "Error" CRESET " :: Missing #bytecode directive.\n");
return false;
}
{
Error error;
Error_Init(&error);
testcase->source = Source_FromString("<input>", str + source_offset, source_length, &error);
if(testcase->source == NULL) {
Error_Print(&error, ErrorType_UNSPECIFIED, stderr);
Error_Free(&error);
return false;
}
testcase->bytecode = Source_FromString("<output>", str + bytecode_offset, bytecode_length, &error);
if(testcase->bytecode == NULL) {
Error_Print(&error, ErrorType_UNSPECIFIED, stderr);
Source_Free(testcase->source);
Error_Free(&error);
return false;
}
Error_Free(&error);
}
return true;
}
static Executable *compile_source_and_print_error_on_failure(Source *src)
{
int error_offset;
Error error;
Error_Init(&error);
Executable *exe = compile(src, &error, &error_offset);
if(exe == NULL) {
Error_Print(&error, ErrorType_UNSPECIFIED, stderr);
Error_Free(&error);
return NULL;
}
Error_Free(&error);
return exe;
}
typedef enum {
TestResult_PASSED,
TestResult_FAILED,
TestResult_ABORT,
} TestResult;
static TestResult runTest(const char *file)
{
Source *source;
{
Error error;
Error_Init(&error);
source = Source_FromFile(file, &error);
if(source == NULL) {
Error_Print(&error, ErrorType_UNSPECIFIED, stderr);
Error_Free(&error);
return TestResult_ABORT;
}
Error_Free(&error);
}
TestCase testcase;
{
if(!parseTestCaseSource(source, &testcase)) {
Source_Free(source);
return TestResult_ABORT;
}
}
Source_Free(source);
Executable *exe1 = compile_source_and_print_error_on_failure(testcase.source);
if(exe1 == NULL) {
Source_Free(testcase.source);
Source_Free(testcase.bytecode);
return TestResult_ABORT;
}
Executable *exe2;
{
Error error;
Error_Init(&error);
int error_offset;
exe2 = assemble(testcase.bytecode, &error, &error_offset);
if(exe2 == NULL) {
Error_Print(&error, ErrorType_UNSPECIFIED, stderr);
Error_Free(&error);
Executable_Free(exe1);
Source_Free(testcase.source);
Source_Free(testcase.bytecode);
return TestResult_ABORT;
}
Error_Free(&error);
}
bool passed = Executable_Equiv(exe1, exe2, stderr, " " WHT "Log" CRESET " :: ");
Executable_Free(exe1);
Executable_Free(exe2);
Source_Free(testcase.source);
Source_Free(testcase.bytecode);
return passed
? TestResult_PASSED
: TestResult_FAILED;
}
static void runTestSuite(const char *folder)
{
DIR *handle = opendir(folder);
if(handle == NULL) {
fprintf(stderr, RED "Error" CRESET " :: Failed to open folder '%s'.\n", folder);
return;
}
size_t total_tests = 0;
size_t passed_tests = 0;
size_t failed_tests = 0;
size_t aborted_tests = 0;
size_t skipped_files = 0;
size_t folder_len = strlen(folder);
// If the folder path ends with a "/",
// but it's not the first character,
// then pop it.
if(folder_len > 0 && folder[folder_len-1] == '/')
folder_len -= 1;
// Now we know for sure that the provided
// folder needs a trailing "/".
char test_path[1024];
// Make sure the buffer can contain the
// folder's name (plus the trailing "/"
// and the null byte).
if(folder_len+2 > sizeof(test_path)) {
fprintf(stderr, RED "Error" CRESET " :: Suite folder name is too long\n");
return;
}
memcpy(test_path, folder, folder_len);
test_path[folder_len] = '/';
test_path[folder_len+1] = '\0';
// Now count the "/".
folder_len += 1;
struct dirent *dir;
while((dir = readdir(handle)) != NULL) {
const char *name = dir->d_name;
size_t name_len = strlen(name);
if(dir->d_type != DT_REG) {
fprintf(stderr, " " WHT "Log" CRESET " :: Skipping '%s' (not a regular file).\n", name);
skipped_files += 1;
continue;
}
if(folder_len + name_len >= sizeof(test_path)) {
fprintf(stderr, " " WHT "Log" CRESET " :: Skipping '%s' (path is too long).\n", name);
skipped_files += 1;
continue;
}
// Remove the previous file's name
// by truncating the parent folder
// name with a null byte.
test_path[folder_len] = '\0';
strcat(test_path, name);
TestResult res = runTest(test_path);
switch(res) {
case TestResult_PASSED:
fprintf(stdout, " " BLU "Test" CRESET " :: Passed (%s)\n", name);
passed_tests += 1;
break;
case TestResult_FAILED:
fprintf(stdout, " " YEL "Test" CRESET " :: Failed (%s)\n", name);
failed_tests += 1;
break;
case TestResult_ABORT:
fprintf(stdout, " " RED "Test" CRESET " :: Aborted (%s)\n", name);
aborted_tests += 1;
break;
}
total_tests += 1;
}
fprintf(stdout, "\n");
fprintf(stdout, "@-----SUMMARY-----@\n");
fprintf(stdout, "| Total : %-3ld |\n", total_tests);
fprintf(stdout, "| Passed : %-3ld |\n", passed_tests);
fprintf(stdout, "| Failed : %-3ld |\n", failed_tests);
fprintf(stdout, "| Aborted : %-3ld |\n", aborted_tests);
fprintf(stdout, "| Skipped : %-3ld |\n", skipped_files);
fprintf(stdout, "@-----------------@\n");
closedir(handle);
}
int main(int argc, char **argv)
{
if(argc < 2) {
fprintf(stderr, RED "Error" CRESET " :: No test suite folder name was provided.\n");
fprintf(stderr, WHT "Usage" CRESET " :: %s <path>\n", argv[0]);
return -1;
}
const char *folder = argv[1];
runTestSuite(folder);
return 0;
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include "main.h"
#include "run.h"
#include "../lib/run.h"
#include "../lib/runtime.h"
#include "../lib/executable.h"
#include "../lib/compiler/compile.h"
#include "../lib/assembler/assemble.h"
static TestResult runCompilerTest(const char *inputs[static 2], FILE *log_stream);
static TestResult runRuntimeTest(const char *inputs[static 2], FILE *log_stream);
static const TestType test_types[] = {
{.name="compiler", .routine=runCompilerTest, .fields=(const char*[]){"source", "bytecode", NULL}},
{.name="runtime", .routine=runRuntimeTest, .fields=(const char*[]){"bytecode", "output", NULL}},
{.name=NULL, .fields=NULL, .routine=NULL},
};
int main(int argc, char **argv)
{
TestBatchResults results = {.passed=0, .failed=0, .aborted=0, .batch_aborted=false};
for (int i = 1; i < argc; i++) {
const char *dir_name = argv[i];
TestBatchResults temp = runTestDirectory(test_types, dir_name, stderr);
if (temp.batch_aborted)
fprintf(stderr, "Error: Failed to run test batch\n");
else {
results.passed += temp.passed;
results.failed += temp.failed;
results.aborted += temp.aborted;
}
}
fprintf(stderr, "SUMMARY: %ld passed, %ld failed, %ld aborted\n",
results.passed, results.failed, results.aborted);
return 0;
}
static TestResult runCompilerTest(const char *inputs[static 2], FILE *log_stream)
{
Error error;
Error_Init(&error);
Source *srcA = Source_FromString("<test>", inputs[0], -1, &error);
if (srcA == NULL) {
Error_Print(&error, ErrorType_UNSPECIFIED, log_stream);
Error_Free(&error);
return TestResult_ABORTED;
}
Source *srcB = Source_FromString("<test>", inputs[1], -1, &error);
if (srcA == NULL) {
Error_Print(&error, ErrorType_UNSPECIFIED, log_stream);
Error_Free(&error);
Source_Free(srcA);
return TestResult_ABORTED;
}
int error_offset;
Executable *exeA = compile(srcA, &error, &error_offset);
if (exeA == NULL) {
Error_Print(&error, ErrorType_UNSPECIFIED, log_stream);
Error_Free(&error);
Source_Free(srcA);
Source_Free(srcB);
return TestResult_ABORTED;
}
Executable *exeB = assemble(srcB, &error, &error_offset);
if (exeB == NULL) {
Error_Print(&error, ErrorType_UNSPECIFIED, log_stream);
Error_Free(&error);
Source_Free(srcA);
Source_Free(srcB);
Executable_Free(exeA);
return TestResult_ABORTED;
}
bool passed = Executable_Equiv(exeA, exeB, log_stream, "Info: ");
Source_Free(srcA);
Source_Free(srcB);
Executable_Free(exeA);
Executable_Free(exeB);
return passed ? TestResult_PASSED : TestResult_FAILED;
}
static TestResult runRuntimeTest(const char *inputs[static 2], FILE *log_stream)
{
char buffer[1024];
FILE *output_stream = fmemopen(buffer, sizeof(buffer), "wb");
if (output_stream == NULL)
return TestResult_ABORTED;
RuntimeConfig config = Runtime_GetDefaultConfigs();
config.stdout = output_stream;
Runtime *runtime = Runtime_New(config);
if (runtime == NULL) {
fclose(output_stream);
return TestResult_ABORTED;
}
Error error;
Error_Init(&error);
if (!Runtime_plugDefaultBuiltins(runtime, &error)) {
Error_Print(&error, ErrorType_UNSPECIFIED, log_stream);
Error_Free(&error);
Runtime_PrintStackTrace(runtime, log_stream);
Runtime_Free(runtime);
fclose(output_stream);
return TestResult_ABORTED;
}
if (!runBytecodeString(runtime, inputs[0], &error)) {
Error_Print(&error, ErrorType_UNSPECIFIED, log_stream);
Error_Free(&error);
Runtime_PrintStackTrace(runtime, log_stream);
Runtime_Free(runtime);
fclose(output_stream);
return TestResult_ABORTED;
}
fclose(output_stream);
TestResult result;
if (!strcmp(buffer, inputs[1])) // No bounds checks
result = TestResult_PASSED;
else {
result = TestResult_FAILED;
fprintf(stderr, "Error: Expected output [%s] but got [%s]\n", inputs[1], buffer);
}
Error_Free(&error);
Runtime_Free(runtime);
return result;
}
+8
View File
@@ -0,0 +1,8 @@
#ifndef MAIN_H
#define MAIN_H
typedef enum {
TestResult_ABORTED,
TestResult_FAILED,
TestResult_PASSED,
} TestResult;
#endif
BIN
View File
Binary file not shown.
+210
View File
@@ -0,0 +1,210 @@
#include <errno.h>
#include <string.h>
#include <dirent.h>
#include <stdlib.h>
#include "run.h"
#include "test.h"
static const TestType *findTestType(const TestType *test_types, const char *name)
{
for (size_t i = 0; test_types[i].name != NULL; i++)
if (!strcmp(name, test_types[i].name))
return test_types + i;
return NULL;
}
TestResult runTestString(const TestType *test_types, const char *src, FILE *log_stream)
{
NojaTest test;
ErrorID error = parseTest(src, &test);
if (error != ErrorID_VOID) {
fprintf(log_stream, "Error: %s\n", getErrorString(error));
return TestResult_ABORTED;
}
const char *type = queryTest(&test, "type");
if (type == NULL) {
fprintf(log_stream, "Error: Test is missing the \"type\" field\n");
return TestResult_ABORTED;
}
const TestType *info = findTestType(test_types, type);
if (info == NULL) {
fprintf(log_stream, "Error: Invalid test type \"%s\"\n", type);
return TestResult_ABORTED;
}
bool uses_type = false;
const char *ordered_fields[8];
size_t i = 0;
while (info->fields[i] != NULL) {
uses_type = uses_type || !strcmp(info->fields[i], "type");
const char *field = queryTest(&test, info->fields[i]);
if (field == NULL) {
fprintf(log_stream, "Error: Test is missing the \"%s\" field, necessary for \"%s\" tests\n", info->fields[i], info->name);
return TestResult_ABORTED;
}
if (i == sizeof(ordered_fields)/sizeof(ordered_fields[0]))
abort();
ordered_fields[i] = field;
i++;
}
size_t used_fields = i;
size_t unused_fields = test.fields_count - used_fields;
if (!uses_type) unused_fields--;
if (unused_fields > 0)
fprintf(log_stream, "Warning: %ld field of the test weren't used\n", unused_fields);
TestResult result = info->routine(ordered_fields, log_stream);
freeTest(&test);
return result;
}
static char *getStreamData(FILE *file_stream, FILE *log_stream)
{
size_t copied = 0, just_copied;
size_t capacity = 512;
char *content = NULL;
do {
capacity *= 2;
content = realloc(content, capacity);
if (content == NULL) {
fprintf(log_stream, "Error: Out of memory\n");
return NULL;
}
just_copied = fread(content + copied, 1, capacity - copied, file_stream);
copied += just_copied;
} while (just_copied > 0);
if (ferror(file_stream)) {
free(content);
fprintf(log_stream, "Error: Couldn't read file contents\n");
return NULL;
}
if (copied+1 >= capacity) {
content = realloc(content, capacity+1);
if (content == NULL) {
fprintf(log_stream, "Error: Out of memory");
return NULL;
}
}
content[copied] = '\0';
return content;
}
TestResult runTestFile(const TestType *test_types, const char *file, FILE *log_stream)
{
FILE *file_stream = fopen(file, "rb");
if (file_stream == NULL) {
fprintf(log_stream, "Error: Couldn't open file \"%s\" (%s)\n", file, strerror(errno));
return TestResult_ABORTED;
}
char *data = getStreamData(file_stream, log_stream);
if (data == NULL) {
fclose(file_stream);
return TestResult_ABORTED;
}
TestResult result = runTestString(test_types, data, log_stream);
free(data);
fclose(file_stream);
return result;
}
static const char *getExtension(const char *file)
{
int dot = -1;
for (int i = 0; file[i] != '\0'; i++)
if (file[i] == '.')
dot = i;
return (dot == -1) ? "" : file + dot;
}
TestBatchResults runTestDirectory(const TestType *test_types, const char *dir_name, FILE *log_stream)
{
TestBatchResults results = {.passed=0, .failed=0, .aborted=0, .batch_aborted=false};
DIR *d = opendir(dir_name);
if (d == NULL) {
results.batch_aborted = true;
return results;
}
struct dirent *ent;
while ((ent = readdir(d)) != NULL) {
if (ent->d_name[0] == '.') {
//fprintf(log_stream, "Info: Ignoring file \"%s\"\n", ent->d_name);
continue;
}
char full[1024];
strcpy(full, dir_name);
if (dir_name[strlen(dir_name)-1] != '/')
strcat(full, "/");
strcat(full, ent->d_name);
if (ent->d_type == DT_DIR) {
TestBatchResults sub_results = runTestDirectory(test_types, full, log_stream);
if (!sub_results.batch_aborted) {
results.passed += sub_results.passed;
results.failed += sub_results.failed;
results.aborted += sub_results.aborted;
}
continue;
}
if (ent->d_type == DT_UNKNOWN) {
abort();
}
if (ent->d_type != DT_REG) {
//fprintf(log_stream, "Info: Ignoring file \"%s\"\n", ent->d_name);
continue;
}
const char *ext = getExtension(ent->d_name);
if (strcmp(ext, ".noja-test")) {
//fprintf(log_stream, "Info: Ignoring file \"%s\"\n", ent->d_name);
continue;
}
TestResult result = runTestFile(test_types, full, log_stream);
switch (result) {
case TestResult_PASSED:
fprintf(log_stream, "Test %s .. PASSED\n", full);
results.passed++;
break;
case TestResult_FAILED:
fprintf(log_stream, "Test %s .. FAILED\n", full);
results.failed++;
break;
case TestResult_ABORTED:
fprintf(log_stream, "Test %s .. ABORTED\n", full);
results.aborted++;
break;
}
}
closedir(d);
return results;
}
+21
View File
@@ -0,0 +1,21 @@
#include <stdio.h>
#include <stddef.h>
#include <stdbool.h>
#include "main.h"
typedef struct {
const char *name;
const char **fields;
TestResult (*routine)(const char **fields, FILE *log_stream);
} TestType;
typedef struct {
size_t passed;
size_t failed;
size_t aborted;
bool batch_aborted;
} TestBatchResults;
TestResult runTestFile(const TestType *test_types, const char *file, FILE *log_stream);
TestResult runTestString(const TestType *test_types, const char *src, FILE *log_stream);
TestBatchResults runTestDirectory(const TestType *test_types, const char *dir_name, FILE *log_stream);
BIN
View File
Binary file not shown.
+65
View File
@@ -0,0 +1,65 @@
#include "scanner.h"
bool followsCharacterThat(NojaTestScanner *scanner, int (*testfn)(int c))
{
return scanner->cur < scanner->len && testfn(scanner->src[scanner->cur]);
}
bool followsCharacter(NojaTestScanner *scanner, char c)
{
return scanner->cur < scanner->len && scanner->src[scanner->cur] == c;
}
bool noMoreCharacters(NojaTestScanner *scanner)
{
return scanner->cur == scanner->len;
}
bool ifFollowsConsumeCharacter(NojaTestScanner *scanner, char c)
{
bool follows = followsCharacter(scanner, c);
if (follows)
scanner->cur++;
return follows;
}
void consumeCharactersThat(NojaTestScanner *scanner, int (*testfn)(int c))
{
while (followsCharacterThat(scanner, testfn))
scanner->cur++;
}
/*
#ifndef NDEBUG
bool noMoreCharacters__wrapped(NojaTestScanner *scanner, const char *file, size_t line)
{
fprintf(stderr, "noMoreCharacters(%p) - %s:%ld\n", scanner, file, line);
return noMoreCharacters(scanner);
}
bool followsCharacter__wrapped(NojaTestScanner *scanner, char c, const char *file, size_t line)
{
fprintf(stderr, "followsCharacter(%p, '%c') - %s:%ld\n", scanner, c, file, line);
return followsCharacter(scanner, c);
}
bool followsCharacterThat__wrapped(NojaTestScanner *scanner, int (*testfn)(int c), const char *file, size_t line)
{
fprintf(stderr, "followsCharacterThat(%p, %p) - %s:%ld\n", scanner, testfn, file, line);
return followsCharacterThat(scanner);
}
void consumeCharactersThat__wrapped(NojaTestScanner *scanner, int (*testfn)(int c), const char *file, size_t line)
{
fprintf(stderr, "consumeCharactersThat(%p, %p) - %s:%ld\n", scanner, testfn, file, line);
return consumeCharactersThat(scanner);
}
bool ifFollowsConsumeCharacter__wrapped(NojaTestScanner *scanner, char c, const char *file, size_t line)
{
fprintf(stderr, "ifFollowsConsumeCharacter(%p, '%c') - %s:%ld\n", scanner, c, file, line);
return ifFollowsConsumeCharacter(scanner);
}
#endif
*/
+32
View File
@@ -0,0 +1,32 @@
#ifndef TEST_SCANNER_H
#define TEST_SCANNER_H
#include <stddef.h>
#include <stdbool.h>
typedef struct {
const char *src;
size_t cur, len;
} NojaTestScanner;
bool noMoreCharacters(NojaTestScanner *scanner);
bool followsCharacter(NojaTestScanner *scanner, char c);
bool followsCharacterThat(NojaTestScanner *scanner, int (*testfn)(int c));
void consumeCharactersThat(NojaTestScanner *scanner, int (*testfn)(int c));
bool ifFollowsConsumeCharacter(NojaTestScanner *scanner, char c);
/*
#ifndef NDEBUG
bool noMoreCharacters__wrapped(NojaTestScanner *scanner, const char *file, size_t line);
bool followsCharacter__wrapped(NojaTestScanner *scanner, char c, const char *file, size_t line);
bool followsCharacterThat__wrapped(NojaTestScanner *scanner, int (*testfn)(int c), const char *file, size_t line);
void consumeCharactersThat__wrapped(NojaTestScanner *scanner, int (*testfn)(int c), const char *file, size_t line);
bool ifFollowsConsumeCharacter__wrapped(NojaTestScanner *scanner, char c, const char *file, size_t line);
#define noMoreCharacters(scanner) noMoreCharacters__wrapped(scanner, __FILE__, __LINE__)
#define followsCharacter(scanner, c) followsCharacter__wrapped(scanner, c, __FILE__, __LINE__)
#define followsCharacterThat(scanner, testfn) followsCharacterThat__wrapped(scanner, testfn, __FILE__, __LINE__)
#define consumeCharactersThat(scanner, testfn) consumeCharacterThat__wrapped(scanner, testfn, __FILE__, __LINE__)
#define ifFollowsConsumeCharacter(scanner, c) ifFollowsConsumeCharacter__wrapped(scanner, c, __FILE__, __LINE__)
#endif
*/
#endif
Binary file not shown.
+86
View File
@@ -0,0 +1,86 @@
#include <stdlib.h>
#include <string.h>
#include "test.h"
#include "error.h"
#include "field.h"
static bool growFieldsArray(NojaTest *test)
{
size_t new_capacity = 2 * test->fields_capacity;
NojaTestField *new_fields;
if (test->fields == test->fields_maybe) {
new_fields = malloc(new_capacity * sizeof(NojaTestField));
if (new_fields != NULL)
memcpy(new_fields, test->fields, test->fields_count * sizeof(NojaTestField));
} else
new_fields = realloc(test->fields, new_capacity * sizeof(NojaTestField));
if (new_fields == NULL)
return false;
test->fields = new_fields;
test->fields_capacity = new_capacity;
return true;
}
void freeTest(NojaTest *test)
{
for (size_t i = 0; i < test->fields_count; i++)
freeField(test->fields + i);
if (test->fields != test->fields_maybe)
free(test->fields);
}
const char *queryTest(NojaTest *test, const char *name)
{
for (size_t i = 0; i < test->fields_count; i++)
if (!strcmp(name, test->fields[i].name))
return test->fields[i].body;
return NULL;
}
ErrorID parseTest(const char *src, NojaTest *test)
{
NojaTestScanner scanner = {.src=src, .len=strlen(src), .cur=0};
test->fields = test->fields_maybe;
test->fields_count = 0;
test->fields_capacity = sizeof(test->fields_maybe)/sizeof(test->fields_maybe[0]);
bool done = false;
do {
if (test->fields_count == test->fields_capacity)
if (!growFieldsArray(test)) {
freeTest(test);
return ErrorID_OUTOFMEMORY;
}
NojaTestField *field = test->fields + test->fields_count;
ErrorID error = parseField(&scanner, field);
switch (error) {
case ErrorID_VOID:
// Before acknowledging the insertion
// by incrementing the field counter,
// check that the new field doesn't have
// a duplicate name
if (queryTest(test, field->name))
return ErrorID_DUPLFIELD;
test->fields_count++;
break;
case ErrorID_BADSYNTAX_NOFIELD:
done = true;
break;
default:
freeTest(test);
return error;
}
} while (!done);
return ErrorID_VOID;
}
+12
View File
@@ -0,0 +1,12 @@
#include "field.h"
#include "error.h"
typedef struct {
NojaTestField fields_maybe[8];
NojaTestField *fields;
size_t fields_count;
size_t fields_capacity;
} NojaTest;
ErrorID parseTest(const char *src, NojaTest *test);
const char *queryTest(NojaTest *test, const char *name);
void freeTest(NojaTest *test);
BIN
View File
Binary file not shown.
Executable
BIN
View File
Binary file not shown.
@@ -1,11 +1,12 @@
#source
@type [compiler]
@source
do
{}
while none;
#bytecode
@bytecode
begin:
PUSHNNE;
@@ -1,12 +1,13 @@
#source
@type [compiler]
@source
do
true;
while none;
false;
#bytecode
@bytecode
begin:
PUSHTRU;
@@ -1,5 +1,6 @@
#source
@type [compiler]
@source
do {
true;
@@ -7,7 +8,7 @@
} while none;
false;
#bytecode
@bytecode
begin:
PUSHTRU;
@@ -1,5 +1,6 @@
#source
@type [compiler]
@source
do {
true;
@@ -8,7 +9,7 @@
} while none;
false;
#bytecode
@bytecode
begin:
PUSHTRU;
@@ -1,12 +1,13 @@
#source
@type [compiler]
@source
do
break;
while none;
false;
#bytecode
@bytecode
begin:
JUMP end;
@@ -1,9 +1,10 @@
#source
@type [compiler]
@source
1 + 7;
#bytecode
@bytecode
PUSHINT 1;
PUSHINT 7;
@@ -1,9 +1,10 @@
#source
@type [compiler]
@source
1 - 7;
#bytecode
@bytecode
PUSHINT 1;
PUSHINT 7;
@@ -1,9 +1,10 @@
#source
@type [compiler]
@source
1 * 7;
#bytecode
@bytecode
PUSHINT 1;
PUSHINT 7;
@@ -1,9 +1,10 @@
#source
@type [compiler]
@source
1 / 7;
#bytecode
@bytecode
PUSHINT 1;
PUSHINT 7;
@@ -1,12 +1,13 @@
#source
@type [compiler]
@source
1.0 + 7;
#bytecode
@bytecode
PUSHFLT 1.0;
PUSHINT 7;
ADD;
POP 1;
EXIT;
EXIT;
@@ -1,9 +1,10 @@
#source
@type [compiler]
@source
1.0 - 7;
#bytecode
@bytecode
PUSHFLT 1.0;
PUSHINT 7;
@@ -1,9 +1,10 @@
#source
@type [compiler]
@source
1.0 * 7;
#bytecode
@bytecode
PUSHFLT 1.0;
PUSHINT 7;
@@ -1,9 +1,10 @@
#source
@type [compiler]
@source
1.0 / 7;
#bytecode
@bytecode
PUSHFLT 1.0;
PUSHINT 7;
@@ -1,9 +1,10 @@
#source
@type [compiler]
@source
1 + 7.0;
#bytecode
@bytecode
PUSHINT 1;
PUSHFLT 7.0;
@@ -1,9 +1,10 @@
#source
@type [compiler]
@source
1 - 7.0;
#bytecode
@bytecode
PUSHINT 1;
PUSHFLT 7.0;
@@ -1,9 +1,10 @@
#source
@type [compiler]
@source
1 * 7.0;
#bytecode
@bytecode
PUSHINT 1;
PUSHFLT 7.0;
@@ -1,9 +1,10 @@
#source
@type [compiler]
@source
1 / 7.0;
#bytecode
@bytecode
PUSHINT 1;
PUSHFLT 7.0;
@@ -1,9 +1,10 @@
#source
@type [compiler]
@source
1 + 2 * 3;
#bytecode
@bytecode
PUSHINT 1;
PUSHINT 2;
@@ -1,9 +1,10 @@
#source
@type [compiler]
@source
1 * 2 + 3;
#bytecode
@bytecode
PUSHINT 1;
PUSHINT 2;
@@ -1,9 +1,10 @@
#source
@type [compiler]
@source
(1 + 2) * 3;
#bytecode
@bytecode
PUSHINT 1;
PUSHINT 2;
@@ -1,9 +1,10 @@
#source
@type [compiler]
@source
1 + (2 * 3);
#bytecode
@bytecode
PUSHINT 1;
PUSHINT 2;
@@ -1,9 +1,10 @@
#source
@type [compiler]
@source
1 * 2 * 3 * 4;
#bytecode
@bytecode
PUSHINT 1;
PUSHINT 2;
@@ -1,9 +1,10 @@
#source
@type [compiler]
@source
1 * (2 * (3 * 4));
#bytecode
@bytecode
PUSHINT 1;
PUSHINT 2;
@@ -1,9 +1,10 @@
#source
@type [compiler]
@source
1 + 2 * 3 + 4;
#bytecode
@bytecode
PUSHINT 1;
PUSHINT 2;
@@ -1,9 +1,10 @@
#source
@type [compiler]
@source
1 * 2 + 3 * 4;
#bytecode
@bytecode
PUSHINT 1;
PUSHINT 2;
@@ -1,10 +1,11 @@
#source
@type [compiler]
@source
A - B - C;
A - B * C;
#bytecode
@bytecode
PUSHVAR "A";
PUSHVAR "B";
@@ -0,0 +1,16 @@
@type [compiler]
@source
# This comment is because there
# can't be a '[' right before a
# tag.
[1];
@bytecode
PUSHLST 1;
PUSHINT 0;
PUSHINT 1;
INSERT;
POP 1;
EXIT;
@@ -1,9 +1,12 @@
#source
@type [compiler]
@source
# This comment is because there
# can't be a '[' right before a
# tag.
[true, false];
#bytecode
@bytecode
PUSHLST 2;
@@ -1,9 +1,10 @@
#source
@type [compiler]
@source
var = none;
#bytecode
@bytecode
PUSHNNE;
ASS "var";
@@ -1,9 +1,10 @@
#source
@type [compiler]
@source
var[key] = none;
#bytecode
@bytecode
PUSHNNE;
PUSHVAR "var";
@@ -1,9 +1,10 @@
#source
@type [compiler]
@source
A, B = func();
#bytecode
@bytecode
PUSHVAR "func";
CALL 0, 2;
@@ -1,9 +1,10 @@
#source
@type [compiler]
@source
var.key = none;
#bytecode
@bytecode
PUSHNNE;
PUSHVAR "var";
@@ -1,9 +1,10 @@
#source
@type [compiler]
@source
1 and 2;
#bytecode
@bytecode
PUSHINT 1;
JUMPIFNOTANDPOP false;
@@ -1,9 +1,10 @@
#source
@type [compiler]
@source
1 or 2;
#bytecode
@bytecode
PUSHINT 1;
JUMPIFANDPOP true;
@@ -1,9 +1,10 @@
#source
@type [compiler]
@source
A and B or C and D;
#bytecode
@bytecode
PUSHVAR "A";
JUMPIFNOTANDPOP false_0;
@@ -1,9 +1,10 @@
#source
@type [compiler]
@source
X or Y and Z or W;
#bytecode
@bytecode
PUSHVAR "X";
JUMPIFANDPOP true_0;
@@ -1,9 +1,10 @@
#source
@type [compiler]
@source
({'name': 'Francesco'});
#bytecode
@bytecode
PUSHMAP 1;
PUSHSTR "name";
@@ -1,9 +1,10 @@
#source
@type [compiler]
@source
({'name': 'Francesco', 'age': 25});
#bytecode
@bytecode
PUSHMAP 2;
@@ -1,9 +1,10 @@
#source
@type [compiler]
@source
({name: 'Francesco'});
#bytecode
@bytecode
PUSHMAP 1;
PUSHSTR "name";
@@ -1,9 +1,10 @@
#source
@type [compiler]
@source
({name: 'Francesco', age: 25});
#bytecode
@bytecode
PUSHMAP 2;
@@ -1,9 +1,10 @@
#source
@type [compiler]
@source
({+name: 'Francesco'});
#bytecode
@bytecode
PUSHMAP 1;
PUSHVAR "name";
@@ -1,9 +1,10 @@
#source
@type [compiler]
@source
({+name: 'Francesco'});
#bytecode
@bytecode
PUSHMAP 1;
PUSHVAR "name";
@@ -1,9 +1,10 @@
#source
@type [compiler]
@source
func();
#bytecode
@bytecode
PUSHVAR "func";
CALL 0, 1;
@@ -1,9 +1,10 @@
#source
@type [compiler]
@source
func(none);
#bytecode
@bytecode
PUSHNNE;
PUSHVAR "func";
@@ -1,9 +1,10 @@
#source
@type [compiler]
@source
func(true, false);
#bytecode
@bytecode
PUSHFLS;
PUSHTRU;
@@ -1,9 +1,10 @@
#source
@type [compiler]
@source
set[key];
#bytecode
@bytecode
PUSHVAR "set";
PUSHVAR "key";
@@ -1,8 +1,9 @@
#source
@type [compiler]
@source
set["dog", "cow", "cat"];
#bytecode
@bytecode
PUSHVAR "set";
@@ -1,9 +1,10 @@
#source
@type [compiler]
@source
set.key;
#bytecode
@bytecode
PUSHVAR "set";
PUSHSTR "key";
@@ -1,9 +1,10 @@
#source
@type [compiler]
@source
-1;
#bytecode
@bytecode
PUSHINT 1;
NEG;
@@ -1,9 +1,10 @@
#source
@type [compiler]
@source
+1;
#bytecode
@bytecode
PUSHINT 1;
POS;
@@ -1,9 +1,10 @@
#source
@type [compiler]
@source
not 1;
#bytecode
@bytecode
PUSHINT 1;
NOT;
@@ -0,0 +1,9 @@
@type [compiler]
@source
1;
@bytecode
PUSHINT 1;
POP 1;
EXIT;
@@ -1,9 +1,10 @@
@type [compiler]
#source
@source
6.7;
#bytecode
@bytecode
PUSHFLT 6.7;
POP 1;
@@ -1,9 +1,10 @@
@type [compiler]
#source
@source
'Hello, world!';
#bytecode
@bytecode
PUSHSTR "Hello, world!";
POP 1;
@@ -1,9 +1,10 @@
#source
@type [compiler]
@source
true;
#bytecode
@bytecode
PUSHTRU;
POP 1;
@@ -1,9 +1,10 @@
#source
@type [compiler]
@source
false;
#bytecode
@bytecode
PUSHFLS;
POP 1;
@@ -1,9 +1,10 @@
#source
@type [compiler]
@source
none;
#bytecode
@bytecode
PUSHNNE;
POP 1;
@@ -1,9 +1,10 @@
#source
@type [compiler]
@source
({});
#bytecode
@bytecode
PUSHMAP 0;
POP 1;
@@ -0,0 +1,14 @@
@type [compiler]
@source
# This comment is because there
# can't be a '[' right before a
# tag.
[];
@bytecode
PUSHLST 0;
POP 1;
EXIT;
@@ -0,0 +1,9 @@
@type [compiler]
@source
{}
@bytecode
EXIT;
@@ -0,0 +1,11 @@
@type [compiler]
@source
'This \\ is a slash!';
@bytecode
PUSHSTR "This \\ is a slash!";
POP 1;
EXIT;
@@ -0,0 +1,11 @@
@type [compiler]
@source
'This \n is a newline!';
@bytecode
PUSHSTR "This \n is a newline!";
POP 1;
EXIT;
@@ -0,0 +1,11 @@
@type [compiler]
@source
'This \t is an horizontal tab!';
@bytecode
PUSHSTR "This \t is an horizontal tab!";
POP 1;
EXIT;
@@ -0,0 +1,11 @@
@type [compiler]
@source
'This \r is a special character!';
@bytecode
PUSHSTR "This \r is a special character!";
POP 1;
EXIT;
@@ -0,0 +1,11 @@
@type [compiler]
@source
'Look at this \'!';
@bytecode
PUSHSTR "Look at this '!";
POP 1;
EXIT;
@@ -0,0 +1,11 @@
@type [compiler]
@source
"Look at this \"!";
@bytecode
PUSHSTR "Look at this \"!";
POP 1;
EXIT;
@@ -1,9 +1,10 @@
#source
@type [compiler]
@source
1 == 2;
#bytecode
@bytecode
PUSHINT 1;
PUSHINT 2;
@@ -1,9 +1,10 @@
#source
@type [compiler]
@source
1 != 2;
#bytecode
@bytecode
PUSHINT 1;
PUSHINT 2;
@@ -1,9 +1,10 @@
#source
@type [compiler]
@source
1 < 2;
#bytecode
@bytecode
PUSHINT 1;
PUSHINT 2;
@@ -1,9 +1,10 @@
#source
@type [compiler]
@source
1 > 2;
#bytecode
@bytecode
PUSHINT 1;
PUSHINT 2;
@@ -1,9 +1,10 @@
#source
@type [compiler]
@source
1 <= 2;
#bytecode
@bytecode
PUSHINT 1;
PUSHINT 2;
@@ -1,9 +1,10 @@
#source
@type [compiler]
@source
1 >= 2;
#bytecode
@bytecode
PUSHINT 1;
PUSHINT 2;

Some files were not shown because too many files have changed in this diff Show More