mirror of
https://github.com/zekexiao/pocketlang.git
synced 2025-02-05 20:26:53 +08:00
Merge pull request #37 from ThakeeNathees/bug-fixes
controlflow bugs fixed
This commit is contained in:
commit
2c1468f0e8
@ -18,7 +18,8 @@ def CONFIGURE_ENV(env):
|
||||
|
||||
## The executable to run with the current configuration.
|
||||
env.RUN_TARGET = join(variant_dir, 'bin', binary_name)
|
||||
|
||||
env.SOURCE_DIRS = ('../src/', '../src/include/', '../cli/', '../cli/modules/')
|
||||
|
||||
## PocketLang source files
|
||||
PK_SOURCES = Glob(join(variant_dir, 'src/*.c'))
|
||||
|
||||
@ -170,12 +171,11 @@ if env['vsproj']:
|
||||
|
||||
targets = [ env.RUN_TARGET ] * 4
|
||||
variants = ["debug|Win32", "debug|x64", "release|Win32", "release|x64"]
|
||||
source_dirs = ('../src/', '../src/include/', '../cli/', '../cli/modules/')
|
||||
|
||||
env.MSVSProject(
|
||||
target = PROJECT_NAME + env['MSVSPROJECTSUFFIX'],
|
||||
srcs = collect_source_files(source_dirs, ('.c', '.cpp', '.cc', '.cxx')),
|
||||
incs = collect_source_files(source_dirs, ('.h', '.hpp')),
|
||||
srcs = collect_source_files(env.SOURCE_DIRS, ('.c', '.cpp', '.cc', '.cxx')),
|
||||
incs = collect_source_files(env.SOURCE_DIRS, ('.h', '.hpp')),
|
||||
variant = variants,
|
||||
runfile = targets,
|
||||
buildtarget = targets,
|
||||
|
@ -30,5 +30,5 @@
|
||||
/*****************************************************************************/
|
||||
|
||||
void registerModules(PKVM* vm) {
|
||||
registerModulePath(vm);
|
||||
registerModulePath(vm);
|
||||
}
|
||||
|
@ -9,16 +9,13 @@
|
||||
#include <stdio.h> /* defines FILENAME_MAX */
|
||||
|
||||
#include "../thirdparty/cwalk/cwalk.h"
|
||||
#if defined(_MSC_VER)
|
||||
#if defined(_WIN32) && (defined(_MSC_VER) || defined(__TINYC__))
|
||||
#include "../thirdparty/dirent/dirent.h"
|
||||
#else
|
||||
#include <dirent.h>
|
||||
#endif
|
||||
|
||||
// TODO: No error is handled below. I should check for path with size more than
|
||||
// FILENAME_MAX.
|
||||
|
||||
#if defined(_WIN32) || defined(_WIN64) || defined(WINDOWS)
|
||||
#if defined(_WIN32)
|
||||
#include <windows.h>
|
||||
#include <direct.h>
|
||||
#define get_cwd _getcwd
|
||||
@ -27,6 +24,10 @@
|
||||
#define get_cwd getcwd
|
||||
#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.
|
||||
#define TOSTRING(x) #x
|
||||
#define STRINGIFY(x) TOSTRING(x)
|
||||
|
@ -138,8 +138,14 @@
|
||||
|
||||
#define TOSTRING(x) #x
|
||||
#define STRINGIFY(x) TOSTRING(x)
|
||||
|
||||
// Double to string buffer size.
|
||||
#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 */
|
||||
/*****************************************************************************/
|
||||
|
@ -9,10 +9,7 @@
|
||||
#include "buffers.h"
|
||||
#include "utils.h"
|
||||
#include "vm.h"
|
||||
|
||||
#if DEBUG_DUMP_COMPILED_CODE
|
||||
#include "debug.h"
|
||||
#endif
|
||||
#include "debug.h"
|
||||
|
||||
// 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
|
||||
@ -974,17 +971,17 @@ GrammarRule rules[] = { // Prefix Infix Infix Precedence
|
||||
/* TK_STAR */ { NULL, exprBinaryOp, PREC_FACTOR },
|
||||
/* TK_FSLASH */ { NULL, exprBinaryOp, PREC_FACTOR },
|
||||
/* TK_BSLASH */ NO_RULE,
|
||||
/* TK_EQ */ NO_RULE, // exprAssignment, PREC_ASSIGNMENT
|
||||
/* TK_EQ */ NO_RULE,
|
||||
/* TK_GT */ { NULL, exprBinaryOp, PREC_COMPARISION },
|
||||
/* TK_LT */ { NULL, exprBinaryOp, PREC_COMPARISION },
|
||||
/* TK_EQEQ */ { NULL, exprBinaryOp, PREC_EQUALITY },
|
||||
/* TK_NOTEQ */ { NULL, exprBinaryOp, PREC_EQUALITY },
|
||||
/* TK_GTEQ */ { NULL, exprBinaryOp, PREC_COMPARISION },
|
||||
/* TK_LTEQ */ { NULL, exprBinaryOp, PREC_COMPARISION },
|
||||
/* TK_PLUSEQ */ NO_RULE, // exprAssignment, PREC_ASSIGNMENT
|
||||
/* TK_MINUSEQ */ NO_RULE, // exprAssignment, PREC_ASSIGNMENT
|
||||
/* TK_STAREQ */ NO_RULE, // exprAssignment, PREC_ASSIGNMENT
|
||||
/* TK_DIVEQ */ NO_RULE, // exprAssignment, PREC_ASSIGNMENT
|
||||
/* TK_PLUSEQ */ NO_RULE,
|
||||
/* TK_MINUSEQ */ NO_RULE,
|
||||
/* TK_STAREQ */ NO_RULE,
|
||||
/* TK_DIVEQ */ NO_RULE,
|
||||
/* TK_SRIGHT */ { NULL, exprBinaryOp, PREC_BITWISE_SHIFT },
|
||||
/* TK_SLEFT */ { NULL, exprBinaryOp, PREC_BITWISE_SHIFT },
|
||||
/* TK_MODULE */ NO_RULE,
|
||||
@ -1554,17 +1551,17 @@ static void compilerEnterBlock(Compiler* compiler) {
|
||||
compiler->scope_depth++;
|
||||
}
|
||||
|
||||
// Pop all the locals at the [depth] or highter.
|
||||
static void compilerPopLocals(Compiler* compiler, int depth) {
|
||||
// Pop all the locals at the [depth] or highter. Returns the number of locals
|
||||
// that were poppedl
|
||||
static int compilerPopLocals(Compiler* compiler, int depth) {
|
||||
ASSERT(depth > (int)DEPTH_GLOBAL, "Cannot pop global variables.");
|
||||
|
||||
int local = compiler->var_count - 1;
|
||||
while (local >= 0 && compiler->variables[local].depth >= depth) {
|
||||
emitOpcode(compiler, OP_POP);
|
||||
compiler->var_count--;
|
||||
compiler->stack_size--;
|
||||
local--;
|
||||
}
|
||||
return (compiler->var_count - 1) - local;
|
||||
}
|
||||
|
||||
// Exits a block.
|
||||
@ -1572,7 +1569,9 @@ static void compilerExitBlock(Compiler* compiler) {
|
||||
ASSERT(compiler->scope_depth > (int)DEPTH_GLOBAL, "Cannot exit toplevel.");
|
||||
|
||||
// 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--;
|
||||
}
|
||||
|
||||
@ -1617,7 +1616,7 @@ static void emitConstant(Compiler* compiler, Var value) {
|
||||
|
||||
// Update the jump offset.
|
||||
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.");
|
||||
|
||||
_FN->opcodes.data[addr_index] = (offset >> 8) & 0xff;
|
||||
@ -1644,7 +1643,6 @@ typedef enum {
|
||||
BLOCK_FUNC,
|
||||
BLOCK_LOOP,
|
||||
BLOCK_IF,
|
||||
BLOCK_ELIF,
|
||||
BLOCK_ELSE,
|
||||
} BlockType;
|
||||
|
||||
@ -1747,10 +1745,6 @@ static void compileBlockBody(Compiler* compiler, BlockType type) {
|
||||
consumeStartBlock(compiler, TK_THEN);
|
||||
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) {
|
||||
skipNewLines(compiler);
|
||||
|
||||
@ -1764,11 +1758,9 @@ static void compileBlockBody(Compiler* compiler, BlockType type) {
|
||||
skipNewLines(compiler);
|
||||
}
|
||||
|
||||
bool if_body = (type == BLOCK_IF) || (type == BLOCK_ELIF);
|
||||
|
||||
TokenType next = peek(compiler);
|
||||
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);
|
||||
skipNewLines(compiler);
|
||||
@ -2091,7 +2083,7 @@ static void compileExpression(Compiler* compiler) {
|
||||
parsePrecedence(compiler, PREC_LOWEST);
|
||||
}
|
||||
|
||||
static void compileIfStatement(Compiler* compiler) {
|
||||
static void compileIfStatement(Compiler* compiler, bool elif) {
|
||||
|
||||
skipNewLines(compiler);
|
||||
compileExpression(compiler); //< Condition.
|
||||
@ -2100,23 +2092,19 @@ static void compileIfStatement(Compiler* compiler) {
|
||||
|
||||
compileBlockBody(compiler, BLOCK_IF);
|
||||
|
||||
// Elif statement's don't consume 'end' after they end since it's treated as
|
||||
// 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;
|
||||
if (match(compiler, TK_ELIF)) {
|
||||
|
||||
// Jump pass else.
|
||||
emitOpcode(compiler, OP_JUMP);
|
||||
int exit_jump = emitShort(compiler, 0xffff); //< Will be patched.
|
||||
|
||||
// if (false) jump here.
|
||||
patchJump(compiler, ifpatch);
|
||||
compileBlockBody(compiler, BLOCK_ELIF);
|
||||
|
||||
compilerEnterBlock(compiler);
|
||||
compileIfStatement(compiler, true);
|
||||
compilerExitBlock(compiler);
|
||||
|
||||
patchJump(compiler, exit_jump);
|
||||
|
||||
} else if (match(compiler, TK_ELSE)) {
|
||||
@ -2133,6 +2121,8 @@ static void compileIfStatement(Compiler* compiler) {
|
||||
patchJump(compiler, ifpatch);
|
||||
}
|
||||
|
||||
// elif will not consume the 'end' keyword as it'll be leaved to be consumed
|
||||
// by it's 'if'.
|
||||
if (!elif) {
|
||||
skipNewLines(compiler);
|
||||
consume(compiler, TK_END, "Expected 'end' after statement end.");
|
||||
@ -2240,7 +2230,7 @@ static void compileStatement(Compiler* compiler) {
|
||||
compilerPopLocals(compiler, compiler->loop->depth + 1);
|
||||
|
||||
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;
|
||||
|
||||
} else if (match(compiler, TK_CONTINUE)) {
|
||||
@ -2271,7 +2261,7 @@ static void compileStatement(Compiler* compiler) {
|
||||
emitOpcode(compiler, OP_RETURN);
|
||||
}
|
||||
} else if (match(compiler, TK_IF)) {
|
||||
compileIfStatement(compiler);
|
||||
compileIfStatement(compiler, false);
|
||||
|
||||
} else if (match(compiler, TK_WHILE)) {
|
||||
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.
|
||||
#define ARGC ((int)(vm->fiber->sp - vm->fiber->ret) - 1)
|
||||
|
||||
// Set return value.
|
||||
// Set return value and return.
|
||||
#define RET(value) \
|
||||
do { \
|
||||
*(vm->fiber->ret) = value; \
|
||||
@ -65,9 +65,7 @@ void pkModuleAddFunction(PKVM* vm, PkHandle* module, const char* name,
|
||||
|
||||
#define ERR_INVALID_ARG_TYPE(m_type) \
|
||||
do { \
|
||||
/* 12 chars is enought for a 4 byte integer string */ \
|
||||
/* including the negative sign.*/ \
|
||||
char buff[12]; \
|
||||
char buff[STR_INT_BUFF_SIZE]; \
|
||||
sprintf(buff, "%d", arg); \
|
||||
vm->fiber->error = stringFormat(vm, "Expected a " m_type \
|
||||
" at argument $.", buff); \
|
||||
@ -131,7 +129,7 @@ bool pkGetArgValue(PKVM* vm, int arg, PkVarType type, PkVar* value) {
|
||||
|
||||
Var val = ARG(arg);
|
||||
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,
|
||||
"Expected a $ at argument $.", getPkVarTypeName(type), buff);
|
||||
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.
|
||||
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) {
|
||||
double number;
|
||||
if (isNumeric(var, &number)) {
|
||||
double truncated = trunc(number);
|
||||
double truncated = floor(number);
|
||||
if (truncated == number) {
|
||||
*value = (int32_t)(truncated);
|
||||
return true;
|
||||
@ -327,7 +325,6 @@ void coreAssert(PKVM* vm) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (!toBool(ARG1)) {
|
||||
String* msg = NULL;
|
||||
|
||||
@ -423,6 +420,29 @@ void coreStrStrip(PKVM* vm) {
|
||||
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.
|
||||
// ---------------
|
||||
void coreListAppend(PKVM* vm) {
|
||||
@ -634,6 +654,8 @@ void initializeCore(PKVM* vm) {
|
||||
INITALIZE_BUILTIN_FN("str_lower", coreStrLower, 1);
|
||||
INITALIZE_BUILTIN_FN("str_upper", coreStrUpper, 1);
|
||||
INITALIZE_BUILTIN_FN("str_strip", coreStrStrip, 1);
|
||||
INITALIZE_BUILTIN_FN("str_chr", coreStrChr, 1);
|
||||
INITALIZE_BUILTIN_FN("str_ord", coreStrOrd, 1);
|
||||
|
||||
// List functions.
|
||||
INITALIZE_BUILTIN_FN("list_append", coreListAppend, 2);
|
||||
@ -991,7 +1013,7 @@ Var varGetSubscript(PKVM* vm, Var on, Var key) {
|
||||
{
|
||||
int32_t index;
|
||||
String* str = ((String*)obj);
|
||||
if (!validateIngeger(vm, key, &index, "List index")) {
|
||||
if (!validateInteger(vm, key, &index, "List index")) {
|
||||
return VAR_NULL;
|
||||
}
|
||||
if (!validateIndex(vm, index, str->length, "String")) {
|
||||
@ -1005,7 +1027,7 @@ Var varGetSubscript(PKVM* vm, Var on, Var key) {
|
||||
{
|
||||
int32_t index;
|
||||
VarBuffer* elems = &((List*)obj)->elements;
|
||||
if (!validateIngeger(vm, key, &index, "List index")) {
|
||||
if (!validateInteger(vm, key, &index, "List index")) {
|
||||
return VAR_NULL;
|
||||
}
|
||||
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;
|
||||
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;
|
||||
elems->data[index] = value;
|
||||
return;
|
||||
|
@ -26,7 +26,9 @@ const char* getBuiltinFunctionName(PKVM* vm, int index);
|
||||
// otherwise returns NULL.
|
||||
Script* getCoreLib(PKVM* vm, String* name);
|
||||
|
||||
// Operators //////////////////////////////////////////////////////////////////
|
||||
/*****************************************************************************/
|
||||
/* OPERATORS */
|
||||
/*****************************************************************************/
|
||||
|
||||
Var varAdd(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);
|
||||
void varsetSubscript(PKVM* vm, Var on, Var key, Var value);
|
||||
|
||||
|
||||
#endif // CORE_H
|
||||
|
15
src/vm.c
15
src/vm.c
@ -8,10 +8,7 @@
|
||||
#include <math.h>
|
||||
#include "core.h"
|
||||
#include "utils.h"
|
||||
|
||||
#if DEBUG_DUMP_CALL_STACK
|
||||
#include "debug.h" //< Wrap around debug macro.
|
||||
#endif
|
||||
#include "debug.h"
|
||||
|
||||
// Evaluvated to true if a runtime error set on the current fiber.
|
||||
#define HAS_ERROR() (vm->fiber->error != NULL)
|
||||
@ -548,7 +545,7 @@ PkInterpretResult vmRunScript(PKVM* vm, Script* _script) {
|
||||
#if DEBUG_DUMP_CALL_STACK
|
||||
#define DEBUG_CALL_STACK() \
|
||||
do { \
|
||||
system("cls"); \
|
||||
system("cls"); /* FIXME */ \
|
||||
dumpGlobalValues(vm); \
|
||||
dumpStackFrame(vm); \
|
||||
} while (false)
|
||||
@ -747,11 +744,9 @@ PkInterpretResult vmRunScript(PKVM* vm, Script* _script) {
|
||||
|
||||
// -1 argument means multiple number of args.
|
||||
if (fn->arity != -1 && fn->arity != argc) {
|
||||
String* arg_str = toString(vm, VAR_NUM(fn->arity));
|
||||
vmPushTempRef(vm, &arg_str->_super);
|
||||
String* msg = stringFormat(vm, "Expected excatly @ argument(s).",
|
||||
arg_str);
|
||||
vmPopTempRef(vm); // arg_str.
|
||||
char buff[STR_NUM_BUFF_SIZE]; sprintf(buff, "%d", fn->arity);
|
||||
String* msg = stringFormat(vm, "Expected excatly $ argument(s).",
|
||||
buff);
|
||||
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.
|
||||
unreachable = func assert(false, 'Unreachable') end
|
||||
|
||||
@ -29,3 +29,23 @@ elif true
|
||||
end
|
||||
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
|
||||
from lang import write
|
||||
from lang import clock as c
|
||||
from path import abspath, curdir
|
||||
from path import abspath as ap, curdir as cd
|
||||
|
||||
from lang import *
|
||||
from path import *
|
||||
|
||||
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
|
||||
|
||||
## If it has a module name it'll bind to that name.
|
||||
|
@ -8,7 +8,7 @@ INDENTATION = ' | '
|
||||
test_files = [
|
||||
"lang/basics.pk",
|
||||
"lang/functions.pk",
|
||||
"lang/if.pk",
|
||||
"lang/controlflow.pk",
|
||||
"examples/fib.pk",
|
||||
"examples/prime.pk",
|
||||
]
|
||||
|
Loading…
Reference in New Issue
Block a user