mirror of
https://github.com/zekexiao/pocketlang.git
synced 2025-02-11 15:16:41 +08:00
Merge pull request #37 from ThakeeNathees/bug-fixes
controlflow bugs fixed
This commit is contained in:
commit
2c1468f0e8
@ -18,6 +18,7 @@ def CONFIGURE_ENV(env):
|
|||||||
|
|
||||||
## The executable to run with the current configuration.
|
## The executable to run with the current configuration.
|
||||||
env.RUN_TARGET = join(variant_dir, 'bin', binary_name)
|
env.RUN_TARGET = join(variant_dir, 'bin', binary_name)
|
||||||
|
env.SOURCE_DIRS = ('../src/', '../src/include/', '../cli/', '../cli/modules/')
|
||||||
|
|
||||||
## PocketLang source files
|
## PocketLang source files
|
||||||
PK_SOURCES = Glob(join(variant_dir, 'src/*.c'))
|
PK_SOURCES = Glob(join(variant_dir, 'src/*.c'))
|
||||||
@ -170,12 +171,11 @@ if env['vsproj']:
|
|||||||
|
|
||||||
targets = [ env.RUN_TARGET ] * 4
|
targets = [ env.RUN_TARGET ] * 4
|
||||||
variants = ["debug|Win32", "debug|x64", "release|Win32", "release|x64"]
|
variants = ["debug|Win32", "debug|x64", "release|Win32", "release|x64"]
|
||||||
source_dirs = ('../src/', '../src/include/', '../cli/', '../cli/modules/')
|
|
||||||
|
|
||||||
env.MSVSProject(
|
env.MSVSProject(
|
||||||
target = PROJECT_NAME + env['MSVSPROJECTSUFFIX'],
|
target = PROJECT_NAME + env['MSVSPROJECTSUFFIX'],
|
||||||
srcs = collect_source_files(source_dirs, ('.c', '.cpp', '.cc', '.cxx')),
|
srcs = collect_source_files(env.SOURCE_DIRS, ('.c', '.cpp', '.cc', '.cxx')),
|
||||||
incs = collect_source_files(source_dirs, ('.h', '.hpp')),
|
incs = collect_source_files(env.SOURCE_DIRS, ('.h', '.hpp')),
|
||||||
variant = variants,
|
variant = variants,
|
||||||
runfile = targets,
|
runfile = targets,
|
||||||
buildtarget = targets,
|
buildtarget = targets,
|
||||||
|
@ -9,16 +9,13 @@
|
|||||||
#include <stdio.h> /* defines FILENAME_MAX */
|
#include <stdio.h> /* defines FILENAME_MAX */
|
||||||
|
|
||||||
#include "../thirdparty/cwalk/cwalk.h"
|
#include "../thirdparty/cwalk/cwalk.h"
|
||||||
#if defined(_MSC_VER)
|
#if defined(_WIN32) && (defined(_MSC_VER) || defined(__TINYC__))
|
||||||
#include "../thirdparty/dirent/dirent.h"
|
#include "../thirdparty/dirent/dirent.h"
|
||||||
#else
|
#else
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// TODO: No error is handled below. I should check for path with size more than
|
#if defined(_WIN32)
|
||||||
// FILENAME_MAX.
|
|
||||||
|
|
||||||
#if defined(_WIN32) || defined(_WIN64) || defined(WINDOWS)
|
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <direct.h>
|
#include <direct.h>
|
||||||
#define get_cwd _getcwd
|
#define get_cwd _getcwd
|
||||||
@ -27,6 +24,10 @@
|
|||||||
#define get_cwd getcwd
|
#define get_cwd getcwd
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// TODO: No error is handled below. I should check for path with size more than
|
||||||
|
// FILENAME_MAX.
|
||||||
|
|
||||||
|
|
||||||
// TODO: this macros should be moved to a general place of in cli.
|
// TODO: this macros should be moved to a general place of in cli.
|
||||||
#define TOSTRING(x) #x
|
#define TOSTRING(x) #x
|
||||||
#define STRINGIFY(x) TOSTRING(x)
|
#define STRINGIFY(x) TOSTRING(x)
|
||||||
|
@ -138,8 +138,14 @@
|
|||||||
|
|
||||||
#define TOSTRING(x) #x
|
#define TOSTRING(x) #x
|
||||||
#define STRINGIFY(x) TOSTRING(x)
|
#define STRINGIFY(x) TOSTRING(x)
|
||||||
|
|
||||||
|
// Double to string buffer size.
|
||||||
#define STR_NUM_BUFF_SIZE (3 + DBL_MANT_DIG - DBL_MIN_EXP)
|
#define STR_NUM_BUFF_SIZE (3 + DBL_MANT_DIG - DBL_MIN_EXP)
|
||||||
|
|
||||||
|
// Integer to string buffer size (INT_MAX, INT_MIN are 10 characters long, a
|
||||||
|
// negative sign, and a null byte at the end = 12 bytes).
|
||||||
|
#define STR_INT_BUFF_SIZE 12
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
/* INTERNAL TYPE DEFINES */
|
/* INTERNAL TYPE DEFINES */
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
@ -9,10 +9,7 @@
|
|||||||
#include "buffers.h"
|
#include "buffers.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include "vm.h"
|
#include "vm.h"
|
||||||
|
#include "debug.h"
|
||||||
#if DEBUG_DUMP_COMPILED_CODE
|
|
||||||
#include "debug.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// The maximum number of variables (or global if compiling top level script)
|
// The maximum number of variables (or global if compiling top level script)
|
||||||
// to lookup from the compiling context. Also it's limited by it's opcode
|
// to lookup from the compiling context. Also it's limited by it's opcode
|
||||||
@ -974,17 +971,17 @@ GrammarRule rules[] = { // Prefix Infix Infix Precedence
|
|||||||
/* TK_STAR */ { NULL, exprBinaryOp, PREC_FACTOR },
|
/* TK_STAR */ { NULL, exprBinaryOp, PREC_FACTOR },
|
||||||
/* TK_FSLASH */ { NULL, exprBinaryOp, PREC_FACTOR },
|
/* TK_FSLASH */ { NULL, exprBinaryOp, PREC_FACTOR },
|
||||||
/* TK_BSLASH */ NO_RULE,
|
/* TK_BSLASH */ NO_RULE,
|
||||||
/* TK_EQ */ NO_RULE, // exprAssignment, PREC_ASSIGNMENT
|
/* TK_EQ */ NO_RULE,
|
||||||
/* TK_GT */ { NULL, exprBinaryOp, PREC_COMPARISION },
|
/* TK_GT */ { NULL, exprBinaryOp, PREC_COMPARISION },
|
||||||
/* TK_LT */ { NULL, exprBinaryOp, PREC_COMPARISION },
|
/* TK_LT */ { NULL, exprBinaryOp, PREC_COMPARISION },
|
||||||
/* TK_EQEQ */ { NULL, exprBinaryOp, PREC_EQUALITY },
|
/* TK_EQEQ */ { NULL, exprBinaryOp, PREC_EQUALITY },
|
||||||
/* TK_NOTEQ */ { NULL, exprBinaryOp, PREC_EQUALITY },
|
/* TK_NOTEQ */ { NULL, exprBinaryOp, PREC_EQUALITY },
|
||||||
/* TK_GTEQ */ { NULL, exprBinaryOp, PREC_COMPARISION },
|
/* TK_GTEQ */ { NULL, exprBinaryOp, PREC_COMPARISION },
|
||||||
/* TK_LTEQ */ { NULL, exprBinaryOp, PREC_COMPARISION },
|
/* TK_LTEQ */ { NULL, exprBinaryOp, PREC_COMPARISION },
|
||||||
/* TK_PLUSEQ */ NO_RULE, // exprAssignment, PREC_ASSIGNMENT
|
/* TK_PLUSEQ */ NO_RULE,
|
||||||
/* TK_MINUSEQ */ NO_RULE, // exprAssignment, PREC_ASSIGNMENT
|
/* TK_MINUSEQ */ NO_RULE,
|
||||||
/* TK_STAREQ */ NO_RULE, // exprAssignment, PREC_ASSIGNMENT
|
/* TK_STAREQ */ NO_RULE,
|
||||||
/* TK_DIVEQ */ NO_RULE, // exprAssignment, PREC_ASSIGNMENT
|
/* TK_DIVEQ */ NO_RULE,
|
||||||
/* TK_SRIGHT */ { NULL, exprBinaryOp, PREC_BITWISE_SHIFT },
|
/* TK_SRIGHT */ { NULL, exprBinaryOp, PREC_BITWISE_SHIFT },
|
||||||
/* TK_SLEFT */ { NULL, exprBinaryOp, PREC_BITWISE_SHIFT },
|
/* TK_SLEFT */ { NULL, exprBinaryOp, PREC_BITWISE_SHIFT },
|
||||||
/* TK_MODULE */ NO_RULE,
|
/* TK_MODULE */ NO_RULE,
|
||||||
@ -1554,17 +1551,17 @@ static void compilerEnterBlock(Compiler* compiler) {
|
|||||||
compiler->scope_depth++;
|
compiler->scope_depth++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pop all the locals at the [depth] or highter.
|
// Pop all the locals at the [depth] or highter. Returns the number of locals
|
||||||
static void compilerPopLocals(Compiler* compiler, int depth) {
|
// that were poppedl
|
||||||
|
static int compilerPopLocals(Compiler* compiler, int depth) {
|
||||||
ASSERT(depth > (int)DEPTH_GLOBAL, "Cannot pop global variables.");
|
ASSERT(depth > (int)DEPTH_GLOBAL, "Cannot pop global variables.");
|
||||||
|
|
||||||
int local = compiler->var_count - 1;
|
int local = compiler->var_count - 1;
|
||||||
while (local >= 0 && compiler->variables[local].depth >= depth) {
|
while (local >= 0 && compiler->variables[local].depth >= depth) {
|
||||||
emitOpcode(compiler, OP_POP);
|
emitOpcode(compiler, OP_POP);
|
||||||
compiler->var_count--;
|
|
||||||
compiler->stack_size--;
|
|
||||||
local--;
|
local--;
|
||||||
}
|
}
|
||||||
|
return (compiler->var_count - 1) - local;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Exits a block.
|
// Exits a block.
|
||||||
@ -1572,7 +1569,9 @@ static void compilerExitBlock(Compiler* compiler) {
|
|||||||
ASSERT(compiler->scope_depth > (int)DEPTH_GLOBAL, "Cannot exit toplevel.");
|
ASSERT(compiler->scope_depth > (int)DEPTH_GLOBAL, "Cannot exit toplevel.");
|
||||||
|
|
||||||
// Discard all the locals at the current scope.
|
// Discard all the locals at the current scope.
|
||||||
compilerPopLocals(compiler, compiler->scope_depth);
|
int popped = compilerPopLocals(compiler, compiler->scope_depth);
|
||||||
|
compiler->var_count -= popped;
|
||||||
|
compiler->stack_size -= popped;
|
||||||
compiler->scope_depth--;
|
compiler->scope_depth--;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1617,7 +1616,7 @@ static void emitConstant(Compiler* compiler, Var value) {
|
|||||||
|
|
||||||
// Update the jump offset.
|
// Update the jump offset.
|
||||||
static void patchJump(Compiler* compiler, int addr_index) {
|
static void patchJump(Compiler* compiler, int addr_index) {
|
||||||
int offset = (int)_FN->opcodes.count - addr_index - 2;
|
int offset = (int)_FN->opcodes.count - (addr_index + 2 /*bytes index*/);
|
||||||
ASSERT(offset < MAX_JUMP, "Too large address offset to jump to.");
|
ASSERT(offset < MAX_JUMP, "Too large address offset to jump to.");
|
||||||
|
|
||||||
_FN->opcodes.data[addr_index] = (offset >> 8) & 0xff;
|
_FN->opcodes.data[addr_index] = (offset >> 8) & 0xff;
|
||||||
@ -1644,7 +1643,6 @@ typedef enum {
|
|||||||
BLOCK_FUNC,
|
BLOCK_FUNC,
|
||||||
BLOCK_LOOP,
|
BLOCK_LOOP,
|
||||||
BLOCK_IF,
|
BLOCK_IF,
|
||||||
BLOCK_ELIF,
|
|
||||||
BLOCK_ELSE,
|
BLOCK_ELSE,
|
||||||
} BlockType;
|
} BlockType;
|
||||||
|
|
||||||
@ -1747,10 +1745,6 @@ static void compileBlockBody(Compiler* compiler, BlockType type) {
|
|||||||
consumeStartBlock(compiler, TK_THEN);
|
consumeStartBlock(compiler, TK_THEN);
|
||||||
skipNewLines(compiler);
|
skipNewLines(compiler);
|
||||||
|
|
||||||
} else if (type == BLOCK_ELIF) {
|
|
||||||
// Do nothing, because this will be parsed as a new if statement.
|
|
||||||
// and it's condition hasn't parsed yet.
|
|
||||||
|
|
||||||
} else if (type == BLOCK_ELSE) {
|
} else if (type == BLOCK_ELSE) {
|
||||||
skipNewLines(compiler);
|
skipNewLines(compiler);
|
||||||
|
|
||||||
@ -1764,11 +1758,9 @@ static void compileBlockBody(Compiler* compiler, BlockType type) {
|
|||||||
skipNewLines(compiler);
|
skipNewLines(compiler);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool if_body = (type == BLOCK_IF) || (type == BLOCK_ELIF);
|
|
||||||
|
|
||||||
TokenType next = peek(compiler);
|
TokenType next = peek(compiler);
|
||||||
while (!(next == TK_END || next == TK_EOF || (
|
while (!(next == TK_END || next == TK_EOF || (
|
||||||
if_body && (next == TK_ELSE || next == TK_ELIF)))) {
|
(type == BLOCK_IF) && (next == TK_ELSE || next == TK_ELIF)))) {
|
||||||
|
|
||||||
compileStatement(compiler);
|
compileStatement(compiler);
|
||||||
skipNewLines(compiler);
|
skipNewLines(compiler);
|
||||||
@ -2091,7 +2083,7 @@ static void compileExpression(Compiler* compiler) {
|
|||||||
parsePrecedence(compiler, PREC_LOWEST);
|
parsePrecedence(compiler, PREC_LOWEST);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void compileIfStatement(Compiler* compiler) {
|
static void compileIfStatement(Compiler* compiler, bool elif) {
|
||||||
|
|
||||||
skipNewLines(compiler);
|
skipNewLines(compiler);
|
||||||
compileExpression(compiler); //< Condition.
|
compileExpression(compiler); //< Condition.
|
||||||
@ -2100,23 +2092,19 @@ static void compileIfStatement(Compiler* compiler) {
|
|||||||
|
|
||||||
compileBlockBody(compiler, BLOCK_IF);
|
compileBlockBody(compiler, BLOCK_IF);
|
||||||
|
|
||||||
// Elif statement's don't consume 'end' after they end since it's treated as
|
if (match(compiler, TK_ELIF)) {
|
||||||
// else and if they require 2 'end' statements. But we're omitting the 'end'
|
|
||||||
// for the 'else' since it'll consumed by the 'if'.
|
|
||||||
bool elif = false;
|
|
||||||
|
|
||||||
if (peek(compiler) == TK_ELIF) {
|
|
||||||
elif = true;
|
|
||||||
// Override the elif to if so that it'll be parsed as a new if statement
|
|
||||||
// and that's why we're not consuming it here.
|
|
||||||
compiler->current.type = TK_IF;
|
|
||||||
|
|
||||||
// Jump pass else.
|
// Jump pass else.
|
||||||
emitOpcode(compiler, OP_JUMP);
|
emitOpcode(compiler, OP_JUMP);
|
||||||
int exit_jump = emitShort(compiler, 0xffff); //< Will be patched.
|
int exit_jump = emitShort(compiler, 0xffff); //< Will be patched.
|
||||||
|
|
||||||
|
// if (false) jump here.
|
||||||
patchJump(compiler, ifpatch);
|
patchJump(compiler, ifpatch);
|
||||||
compileBlockBody(compiler, BLOCK_ELIF);
|
|
||||||
|
compilerEnterBlock(compiler);
|
||||||
|
compileIfStatement(compiler, true);
|
||||||
|
compilerExitBlock(compiler);
|
||||||
|
|
||||||
patchJump(compiler, exit_jump);
|
patchJump(compiler, exit_jump);
|
||||||
|
|
||||||
} else if (match(compiler, TK_ELSE)) {
|
} else if (match(compiler, TK_ELSE)) {
|
||||||
@ -2133,6 +2121,8 @@ static void compileIfStatement(Compiler* compiler) {
|
|||||||
patchJump(compiler, ifpatch);
|
patchJump(compiler, ifpatch);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// elif will not consume the 'end' keyword as it'll be leaved to be consumed
|
||||||
|
// by it's 'if'.
|
||||||
if (!elif) {
|
if (!elif) {
|
||||||
skipNewLines(compiler);
|
skipNewLines(compiler);
|
||||||
consume(compiler, TK_END, "Expected 'end' after statement end.");
|
consume(compiler, TK_END, "Expected 'end' after statement end.");
|
||||||
@ -2240,7 +2230,7 @@ static void compileStatement(Compiler* compiler) {
|
|||||||
compilerPopLocals(compiler, compiler->loop->depth + 1);
|
compilerPopLocals(compiler, compiler->loop->depth + 1);
|
||||||
|
|
||||||
emitOpcode(compiler, OP_JUMP);
|
emitOpcode(compiler, OP_JUMP);
|
||||||
int patch = emitByte(compiler, 0xffff); //< Will be patched.
|
int patch = emitShort(compiler, 0xffff); //< Will be patched.
|
||||||
compiler->loop->patches[compiler->loop->patch_count++] = patch;
|
compiler->loop->patches[compiler->loop->patch_count++] = patch;
|
||||||
|
|
||||||
} else if (match(compiler, TK_CONTINUE)) {
|
} else if (match(compiler, TK_CONTINUE)) {
|
||||||
@ -2271,7 +2261,7 @@ static void compileStatement(Compiler* compiler) {
|
|||||||
emitOpcode(compiler, OP_RETURN);
|
emitOpcode(compiler, OP_RETURN);
|
||||||
}
|
}
|
||||||
} else if (match(compiler, TK_IF)) {
|
} else if (match(compiler, TK_IF)) {
|
||||||
compileIfStatement(compiler);
|
compileIfStatement(compiler, false);
|
||||||
|
|
||||||
} else if (match(compiler, TK_WHILE)) {
|
} else if (match(compiler, TK_WHILE)) {
|
||||||
compileWhileStatement(compiler);
|
compileWhileStatement(compiler);
|
||||||
|
44
src/core.c
44
src/core.c
@ -49,7 +49,7 @@ void pkModuleAddFunction(PKVM* vm, PkHandle* module, const char* name,
|
|||||||
// Argument count used in variadic functions.
|
// Argument count used in variadic functions.
|
||||||
#define ARGC ((int)(vm->fiber->sp - vm->fiber->ret) - 1)
|
#define ARGC ((int)(vm->fiber->sp - vm->fiber->ret) - 1)
|
||||||
|
|
||||||
// Set return value.
|
// Set return value and return.
|
||||||
#define RET(value) \
|
#define RET(value) \
|
||||||
do { \
|
do { \
|
||||||
*(vm->fiber->ret) = value; \
|
*(vm->fiber->ret) = value; \
|
||||||
@ -65,9 +65,7 @@ void pkModuleAddFunction(PKVM* vm, PkHandle* module, const char* name,
|
|||||||
|
|
||||||
#define ERR_INVALID_ARG_TYPE(m_type) \
|
#define ERR_INVALID_ARG_TYPE(m_type) \
|
||||||
do { \
|
do { \
|
||||||
/* 12 chars is enought for a 4 byte integer string */ \
|
char buff[STR_INT_BUFF_SIZE]; \
|
||||||
/* including the negative sign.*/ \
|
|
||||||
char buff[12]; \
|
|
||||||
sprintf(buff, "%d", arg); \
|
sprintf(buff, "%d", arg); \
|
||||||
vm->fiber->error = stringFormat(vm, "Expected a " m_type \
|
vm->fiber->error = stringFormat(vm, "Expected a " m_type \
|
||||||
" at argument $.", buff); \
|
" at argument $.", buff); \
|
||||||
@ -131,7 +129,7 @@ bool pkGetArgValue(PKVM* vm, int arg, PkVarType type, PkVar* value) {
|
|||||||
|
|
||||||
Var val = ARG(arg);
|
Var val = ARG(arg);
|
||||||
if (pkGetValueType((PkVar)&val) != type) {
|
if (pkGetValueType((PkVar)&val) != type) {
|
||||||
char buff[12]; sprintf(buff, "%d", arg);
|
char buff[STR_NUM_BUFF_SIZE]; sprintf(buff, "%d", arg);
|
||||||
vm->fiber->error = stringFormat(vm,
|
vm->fiber->error = stringFormat(vm,
|
||||||
"Expected a $ at argument $.", getPkVarTypeName(type), buff);
|
"Expected a $ at argument $.", getPkVarTypeName(type), buff);
|
||||||
return false;
|
return false;
|
||||||
@ -215,11 +213,11 @@ static inline bool validateNumeric(PKVM* vm, Var var, double* value,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check if [var] is integer. If not set error and return false.
|
// Check if [var] is integer. If not set error and return false.
|
||||||
static inline bool validateIngeger(PKVM* vm, Var var, int32_t* value,
|
static inline bool validateInteger(PKVM* vm, Var var, int32_t* value,
|
||||||
const char* name) {
|
const char* name) {
|
||||||
double number;
|
double number;
|
||||||
if (isNumeric(var, &number)) {
|
if (isNumeric(var, &number)) {
|
||||||
double truncated = trunc(number);
|
double truncated = floor(number);
|
||||||
if (truncated == number) {
|
if (truncated == number) {
|
||||||
*value = (int32_t)(truncated);
|
*value = (int32_t)(truncated);
|
||||||
return true;
|
return true;
|
||||||
@ -327,7 +325,6 @@ void coreAssert(PKVM* vm) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (!toBool(ARG1)) {
|
if (!toBool(ARG1)) {
|
||||||
String* msg = NULL;
|
String* msg = NULL;
|
||||||
|
|
||||||
@ -423,6 +420,29 @@ void coreStrStrip(PKVM* vm) {
|
|||||||
RET(VAR_OBJ(&newStringLength(vm, start, (uint32_t)(end - start + 1))->_super));
|
RET(VAR_OBJ(&newStringLength(vm, start, (uint32_t)(end - start + 1))->_super));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns the ASCII string value of the integer argument.
|
||||||
|
void coreStrChr(PKVM* vm) {
|
||||||
|
int32_t num;
|
||||||
|
if (!validateInteger(vm, ARG1, &num, "Argument 1"));
|
||||||
|
|
||||||
|
char c = (char)num;
|
||||||
|
RET(VAR_OBJ(&newStringLength(vm, &c, 1)->_super));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns integer value of the given ASCII character.
|
||||||
|
void coreStrOrd(PKVM* vm) {
|
||||||
|
String* c;
|
||||||
|
if (!validateArgString(vm, 1, &c));
|
||||||
|
if (c->length != 1) {
|
||||||
|
vm->fiber->error = newString(vm, "Expected a string of length 1.");
|
||||||
|
RET(VAR_NULL);
|
||||||
|
} else {
|
||||||
|
RET(VAR_NUM((double)c->data[0]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// List functions.
|
// List functions.
|
||||||
// ---------------
|
// ---------------
|
||||||
void coreListAppend(PKVM* vm) {
|
void coreListAppend(PKVM* vm) {
|
||||||
@ -634,6 +654,8 @@ void initializeCore(PKVM* vm) {
|
|||||||
INITALIZE_BUILTIN_FN("str_lower", coreStrLower, 1);
|
INITALIZE_BUILTIN_FN("str_lower", coreStrLower, 1);
|
||||||
INITALIZE_BUILTIN_FN("str_upper", coreStrUpper, 1);
|
INITALIZE_BUILTIN_FN("str_upper", coreStrUpper, 1);
|
||||||
INITALIZE_BUILTIN_FN("str_strip", coreStrStrip, 1);
|
INITALIZE_BUILTIN_FN("str_strip", coreStrStrip, 1);
|
||||||
|
INITALIZE_BUILTIN_FN("str_chr", coreStrChr, 1);
|
||||||
|
INITALIZE_BUILTIN_FN("str_ord", coreStrOrd, 1);
|
||||||
|
|
||||||
// List functions.
|
// List functions.
|
||||||
INITALIZE_BUILTIN_FN("list_append", coreListAppend, 2);
|
INITALIZE_BUILTIN_FN("list_append", coreListAppend, 2);
|
||||||
@ -991,7 +1013,7 @@ Var varGetSubscript(PKVM* vm, Var on, Var key) {
|
|||||||
{
|
{
|
||||||
int32_t index;
|
int32_t index;
|
||||||
String* str = ((String*)obj);
|
String* str = ((String*)obj);
|
||||||
if (!validateIngeger(vm, key, &index, "List index")) {
|
if (!validateInteger(vm, key, &index, "List index")) {
|
||||||
return VAR_NULL;
|
return VAR_NULL;
|
||||||
}
|
}
|
||||||
if (!validateIndex(vm, index, str->length, "String")) {
|
if (!validateIndex(vm, index, str->length, "String")) {
|
||||||
@ -1005,7 +1027,7 @@ Var varGetSubscript(PKVM* vm, Var on, Var key) {
|
|||||||
{
|
{
|
||||||
int32_t index;
|
int32_t index;
|
||||||
VarBuffer* elems = &((List*)obj)->elements;
|
VarBuffer* elems = &((List*)obj)->elements;
|
||||||
if (!validateIngeger(vm, key, &index, "List index")) {
|
if (!validateInteger(vm, key, &index, "List index")) {
|
||||||
return VAR_NULL;
|
return VAR_NULL;
|
||||||
}
|
}
|
||||||
if (!validateIndex(vm, index, (int)elems->count, "List")) {
|
if (!validateIndex(vm, index, (int)elems->count, "List")) {
|
||||||
@ -1063,7 +1085,7 @@ void varsetSubscript(PKVM* vm, Var on, Var key, Var value) {
|
|||||||
{
|
{
|
||||||
int32_t index;
|
int32_t index;
|
||||||
VarBuffer* elems = &((List*)obj)->elements;
|
VarBuffer* elems = &((List*)obj)->elements;
|
||||||
if (!validateIngeger(vm, key, &index, "List index")) return;
|
if (!validateInteger(vm, key, &index, "List index")) return;
|
||||||
if (!validateIndex(vm, index, (int)elems->count, "List")) return;
|
if (!validateIndex(vm, index, (int)elems->count, "List")) return;
|
||||||
elems->data[index] = value;
|
elems->data[index] = value;
|
||||||
return;
|
return;
|
||||||
|
@ -26,7 +26,9 @@ const char* getBuiltinFunctionName(PKVM* vm, int index);
|
|||||||
// otherwise returns NULL.
|
// otherwise returns NULL.
|
||||||
Script* getCoreLib(PKVM* vm, String* name);
|
Script* getCoreLib(PKVM* vm, String* name);
|
||||||
|
|
||||||
// Operators //////////////////////////////////////////////////////////////////
|
/*****************************************************************************/
|
||||||
|
/* OPERATORS */
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
Var varAdd(PKVM* vm, Var v1, Var v2);
|
Var varAdd(PKVM* vm, Var v1, Var v2);
|
||||||
Var varSubtract(PKVM* vm, Var v1, Var v2);
|
Var varSubtract(PKVM* vm, Var v1, Var v2);
|
||||||
@ -43,5 +45,4 @@ void varSetAttrib(PKVM* vm, Var on, String* name, Var value);
|
|||||||
Var varGetSubscript(PKVM* vm, Var on, Var key);
|
Var varGetSubscript(PKVM* vm, Var on, Var key);
|
||||||
void varsetSubscript(PKVM* vm, Var on, Var key, Var value);
|
void varsetSubscript(PKVM* vm, Var on, Var key, Var value);
|
||||||
|
|
||||||
|
|
||||||
#endif // CORE_H
|
#endif // CORE_H
|
||||||
|
15
src/vm.c
15
src/vm.c
@ -8,10 +8,7 @@
|
|||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include "core.h"
|
#include "core.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
#include "debug.h"
|
||||||
#if DEBUG_DUMP_CALL_STACK
|
|
||||||
#include "debug.h" //< Wrap around debug macro.
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Evaluvated to true if a runtime error set on the current fiber.
|
// Evaluvated to true if a runtime error set on the current fiber.
|
||||||
#define HAS_ERROR() (vm->fiber->error != NULL)
|
#define HAS_ERROR() (vm->fiber->error != NULL)
|
||||||
@ -548,7 +545,7 @@ PkInterpretResult vmRunScript(PKVM* vm, Script* _script) {
|
|||||||
#if DEBUG_DUMP_CALL_STACK
|
#if DEBUG_DUMP_CALL_STACK
|
||||||
#define DEBUG_CALL_STACK() \
|
#define DEBUG_CALL_STACK() \
|
||||||
do { \
|
do { \
|
||||||
system("cls"); \
|
system("cls"); /* FIXME */ \
|
||||||
dumpGlobalValues(vm); \
|
dumpGlobalValues(vm); \
|
||||||
dumpStackFrame(vm); \
|
dumpStackFrame(vm); \
|
||||||
} while (false)
|
} while (false)
|
||||||
@ -747,11 +744,9 @@ PkInterpretResult vmRunScript(PKVM* vm, Script* _script) {
|
|||||||
|
|
||||||
// -1 argument means multiple number of args.
|
// -1 argument means multiple number of args.
|
||||||
if (fn->arity != -1 && fn->arity != argc) {
|
if (fn->arity != -1 && fn->arity != argc) {
|
||||||
String* arg_str = toString(vm, VAR_NUM(fn->arity));
|
char buff[STR_NUM_BUFF_SIZE]; sprintf(buff, "%d", fn->arity);
|
||||||
vmPushTempRef(vm, &arg_str->_super);
|
String* msg = stringFormat(vm, "Expected excatly $ argument(s).",
|
||||||
String* msg = stringFormat(vm, "Expected excatly @ argument(s).",
|
buff);
|
||||||
arg_str);
|
|
||||||
vmPopTempRef(vm); // arg_str.
|
|
||||||
RUNTIME_ERROR(msg);
|
RUNTIME_ERROR(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
116
test/examples/brainfuck.pk
Normal file
116
test/examples/brainfuck.pk
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
from lang import write
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## BRAINFUCK IMPLEMENTATION IN POCKETLANG ##
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
## Reference: https://en.wikipedia.org/wiki/Brainfuck
|
||||||
|
|
||||||
|
## Note that this interpreter implementation is just to test pocketlang and is
|
||||||
|
## not an efficient one. This could be optimized by evaluvating the expressions
|
||||||
|
## at "compile time" (AOT) to avoid re-evaluvating those expressions at runtime
|
||||||
|
## and also we can pre compile the loop jump offsets.
|
||||||
|
|
||||||
|
## Source: https://en.wikipedia.org/wiki/Brainfuck
|
||||||
|
hello_world = "++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]
|
||||||
|
>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++."
|
||||||
|
|
||||||
|
## Source: https://github.com/fabianishere/brainfuck/blob/master/examples/asciiart/triangle.bf
|
||||||
|
## Madeby: Nyyrikki(2002)
|
||||||
|
triangle = ">++++[<++++++++>-]>++++++++[>++++<-]>>++>>>+>>>+<<<<<<<<<<[-[->+<]>
|
||||||
|
[-<+>>>.<<]>>>[[->++++++++[>++++<-]>.<<[->+<]+>[->++++++++++<<+>]>.
|
||||||
|
[-]>]]+<<<[-[->+<]+>[-<+>>>-[->+<]++>[-<->]<<<]<<<<]++++++++++.+++.
|
||||||
|
[-]<]+++++"
|
||||||
|
|
||||||
|
## Source: https://github.com/fabianishere/brainfuck/blob/master/examples/math/fib.bf
|
||||||
|
fibonacci = ">++++++++++>+>+[
|
||||||
|
[+++++[>++++++++<-]>.<++++++[>--------<-]+<<<]>.>>[
|
||||||
|
[-]<[>+<-]>>[<<+>+>-]<[>+<-[>+<-[>+<-[>+<-[>+<-[>+<-
|
||||||
|
[>+<-[>+<-[>+<-[>[-]>+>+<<<-[>+<-]]]]]]]]]]]+>>>
|
||||||
|
]<<<
|
||||||
|
]"
|
||||||
|
|
||||||
|
execute(hello_world)
|
||||||
|
execute(triangle)
|
||||||
|
execute(fibonacci)
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## INTERNAL ##
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
def execute(expr)
|
||||||
|
ptr = 0 ## Data pointer.
|
||||||
|
mem = [0] ## Data / Memory
|
||||||
|
|
||||||
|
i = 0 ## Instruction pointer
|
||||||
|
while true
|
||||||
|
c = expr[i] ## Current char.
|
||||||
|
|
||||||
|
## Increment the data pointer (to point to the next cell to the right).
|
||||||
|
if c == '>'
|
||||||
|
ptr += 1
|
||||||
|
if ptr >= mem.length then list_append(mem, 0) end
|
||||||
|
|
||||||
|
## Decrement the data pointer (to point to the next cell to the left).
|
||||||
|
elif c == '<'
|
||||||
|
ptr -= 1
|
||||||
|
if ptr < 0 then assert(false, "ip < 0") end
|
||||||
|
|
||||||
|
## Increment (increase by one) the byte at the data pointer.
|
||||||
|
elif c == '+'
|
||||||
|
if mem[ptr] == 255 then mem[ptr] = 0
|
||||||
|
else mem[ptr] += 1 end
|
||||||
|
|
||||||
|
## Decrement (decrease by one) the byte at the data pointer.
|
||||||
|
elif c == '-'
|
||||||
|
if mem[ptr] == 0 then mem[ptr] = 255
|
||||||
|
else mem[ptr] -= 1 end
|
||||||
|
|
||||||
|
## output the byte at the data pointer.
|
||||||
|
elif c == '.'
|
||||||
|
write(str_chr(mem[ptr]))
|
||||||
|
|
||||||
|
elif c == ','
|
||||||
|
assert(false, "Currently input isn't supported in pocketlang.")
|
||||||
|
|
||||||
|
## if the byte at the data pointer is zero, then instead of moving the
|
||||||
|
## instruction pointer forward to the next command, jump it forward to
|
||||||
|
## the command after the matching ] command.
|
||||||
|
elif c == '[' and mem[ptr] == 0
|
||||||
|
open = 0
|
||||||
|
while true
|
||||||
|
i += 1
|
||||||
|
if expr[i] == ']' and open == 0 then break end
|
||||||
|
|
||||||
|
if expr[i] == '[' then open += 1
|
||||||
|
elif expr[i] == ']' then open -= 1
|
||||||
|
end assert(open >= 0)
|
||||||
|
end
|
||||||
|
|
||||||
|
## if the byte at the data pointer is nonzero, then instead of moving the
|
||||||
|
## instruction pointer forward to the next command, jump it back to the
|
||||||
|
## command after the matching [ command
|
||||||
|
elif c == ']' and mem[ptr] != 0
|
||||||
|
open = 0
|
||||||
|
while true
|
||||||
|
i -= 1
|
||||||
|
if expr[i] == '[' and open == 0 then break end
|
||||||
|
|
||||||
|
if expr[i] == ']' then open -= 1
|
||||||
|
elif expr[i] == '[' then open += 1
|
||||||
|
end assert(open <= 0)
|
||||||
|
end
|
||||||
|
|
||||||
|
else
|
||||||
|
## Any other characters are ignored in brainfuck interpreter.
|
||||||
|
end
|
||||||
|
|
||||||
|
## Increment the instruction pointer by one.
|
||||||
|
## If we reached the end terminate.
|
||||||
|
i += 1
|
||||||
|
if i == expr.length then break end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
@ -1,5 +1,5 @@
|
|||||||
|
|
||||||
## If statement tests.
|
## If statements.
|
||||||
variable = null ## Will be changed by the control flow.
|
variable = null ## Will be changed by the control flow.
|
||||||
unreachable = func assert(false, 'Unreachable') end
|
unreachable = func assert(false, 'Unreachable') end
|
||||||
|
|
||||||
@ -29,3 +29,23 @@ elif true
|
|||||||
end
|
end
|
||||||
assert(variable == 123, 'Nested if statement failed.')
|
assert(variable == 123, 'Nested if statement failed.')
|
||||||
|
|
||||||
|
## While statements
|
||||||
|
|
||||||
|
while true
|
||||||
|
variable = 1212
|
||||||
|
if true then break end
|
||||||
|
end
|
||||||
|
assert(variable == 1212)
|
||||||
|
|
||||||
|
|
||||||
|
while true
|
||||||
|
variable = 22
|
||||||
|
if true then elif false then end
|
||||||
|
if false
|
||||||
|
elif true
|
||||||
|
variable += 2300
|
||||||
|
end
|
||||||
|
variable += 1
|
||||||
|
break
|
||||||
|
end
|
||||||
|
assert(variable == 2323)
|
@ -5,14 +5,12 @@ import lang, path
|
|||||||
import lang as o, path as p
|
import lang as o, path as p
|
||||||
from lang import write
|
from lang import write
|
||||||
from lang import clock as c
|
from lang import clock as c
|
||||||
from path import abspath, curdir
|
|
||||||
from path import abspath as ap, curdir as cd
|
|
||||||
|
|
||||||
from lang import *
|
from lang import *
|
||||||
from path import *
|
from path import *
|
||||||
|
|
||||||
import "basics.pk" ## will import all
|
import "basics.pk" ## will import all
|
||||||
import "if.pk" as if_test
|
import "controlflow.pk" as if_test
|
||||||
from "functions.pk" import fn1, fn2 as f2, fn3
|
from "functions.pk" import fn1, fn2 as f2, fn3
|
||||||
|
|
||||||
## If it has a module name it'll bind to that name.
|
## If it has a module name it'll bind to that name.
|
||||||
|
@ -8,7 +8,7 @@ INDENTATION = ' | '
|
|||||||
test_files = [
|
test_files = [
|
||||||
"lang/basics.pk",
|
"lang/basics.pk",
|
||||||
"lang/functions.pk",
|
"lang/functions.pk",
|
||||||
"lang/if.pk",
|
"lang/controlflow.pk",
|
||||||
"examples/fib.pk",
|
"examples/fib.pk",
|
||||||
"examples/prime.pk",
|
"examples/prime.pk",
|
||||||
]
|
]
|
||||||
|
Loading…
Reference in New Issue
Block a user