mirror of
https://github.com/zekexiao/pocketlang.git
synced 2025-03-04 13:15:55 +08:00
else if change to elif again.
The primary purpose of this change is to disambiguate between `else if` and `else \n if`. Even though it's a breaking change, since it's at the very early state we're allowed to make such breaking syntax change. lang.write function moved to io and io.stdin, io.stdout, io.stderr added for future streamed io operations io.File read, write, open, close, getline, seek, tell implemented str * int multiplication implemented
This commit is contained in:
parent
e16debcff0
commit
6e91b66e69
@ -136,6 +136,7 @@ typedef enum {
|
|||||||
TK_WHILE, // while
|
TK_WHILE, // while
|
||||||
TK_FOR, // for
|
TK_FOR, // for
|
||||||
TK_IF, // if
|
TK_IF, // if
|
||||||
|
TK_ELIF, // elif
|
||||||
TK_ELSE, // else
|
TK_ELSE, // else
|
||||||
TK_BREAK, // break
|
TK_BREAK, // break
|
||||||
TK_CONTINUE, // continue
|
TK_CONTINUE, // continue
|
||||||
@ -200,6 +201,7 @@ static _Keyword _keywords[] = {
|
|||||||
{ "while", 5, TK_WHILE },
|
{ "while", 5, TK_WHILE },
|
||||||
{ "for", 3, TK_FOR },
|
{ "for", 3, TK_FOR },
|
||||||
{ "if", 2, TK_IF },
|
{ "if", 2, TK_IF },
|
||||||
|
{ "elif", 4, TK_ELIF },
|
||||||
{ "else", 4, TK_ELSE },
|
{ "else", 4, TK_ELSE },
|
||||||
{ "break", 5, TK_BREAK },
|
{ "break", 5, TK_BREAK },
|
||||||
{ "continue", 8, TK_CONTINUE },
|
{ "continue", 8, TK_CONTINUE },
|
||||||
@ -1267,7 +1269,8 @@ static void skipNewLines(Compiler* compiler) {
|
|||||||
matchLine(compiler);
|
matchLine(compiler);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Match semi collon, multiple new lines or peek 'end', 'else' keywords.
|
// Match semi collon, multiple new lines or peek 'end', 'else', 'elif'
|
||||||
|
// keywords.
|
||||||
static bool matchEndStatement(Compiler* compiler) {
|
static bool matchEndStatement(Compiler* compiler) {
|
||||||
if (match(compiler, TK_SEMICOLLON)) {
|
if (match(compiler, TK_SEMICOLLON)) {
|
||||||
skipNewLines(compiler);
|
skipNewLines(compiler);
|
||||||
@ -1278,7 +1281,9 @@ static bool matchEndStatement(Compiler* compiler) {
|
|||||||
|
|
||||||
// In the below statement we don't require any new lines or semicolons.
|
// In the below statement we don't require any new lines or semicolons.
|
||||||
// 'if cond then stmnt1 else if cond2 then stmnt2 else stmnt3 end'
|
// 'if cond then stmnt1 else if cond2 then stmnt2 else stmnt3 end'
|
||||||
if (peek(compiler) == TK_END || peek(compiler) == TK_ELSE)
|
if (peek(compiler) == TK_END
|
||||||
|
|| peek(compiler) == TK_ELSE
|
||||||
|
|| peek(compiler) == TK_ELIF)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@ -1639,6 +1644,7 @@ GrammarRule rules[] = { // Prefix Infix Infix Precedence
|
|||||||
/* TK_WHILE */ NO_RULE,
|
/* TK_WHILE */ NO_RULE,
|
||||||
/* TK_FOR */ NO_RULE,
|
/* TK_FOR */ NO_RULE,
|
||||||
/* TK_IF */ NO_RULE,
|
/* TK_IF */ NO_RULE,
|
||||||
|
/* TK_ELIF */ NO_RULE,
|
||||||
/* TK_ELSE */ NO_RULE,
|
/* TK_ELSE */ NO_RULE,
|
||||||
/* TK_BREAK */ NO_RULE,
|
/* TK_BREAK */ NO_RULE,
|
||||||
/* TK_CONTINUE */ NO_RULE,
|
/* TK_CONTINUE */ NO_RULE,
|
||||||
@ -2864,7 +2870,7 @@ static void compileBlockBody(Compiler* compiler, BlockType type) {
|
|||||||
|
|
||||||
_TokenType next = peek(compiler);
|
_TokenType next = peek(compiler);
|
||||||
while (!(next == TK_END || next == TK_EOF ||
|
while (!(next == TK_END || next == TK_EOF ||
|
||||||
((type == BLOCK_IF) && (next == TK_ELSE)))) {
|
((type == BLOCK_IF) && (next == TK_ELSE || next == TK_ELIF)))) {
|
||||||
|
|
||||||
compileStatement(compiler);
|
compileStatement(compiler);
|
||||||
skipNewLines(compiler);
|
skipNewLines(compiler);
|
||||||
@ -3024,7 +3030,7 @@ static void compileExpression(Compiler* compiler) {
|
|||||||
parsePrecedence(compiler, PREC_LOWEST);
|
parsePrecedence(compiler, PREC_LOWEST);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void compileIfStatement(Compiler* compiler, bool else_if) {
|
static void compileIfStatement(Compiler* compiler, bool elif) {
|
||||||
|
|
||||||
skipNewLines(compiler);
|
skipNewLines(compiler);
|
||||||
compileExpression(compiler); //< Condition.
|
compileExpression(compiler); //< Condition.
|
||||||
@ -3033,9 +3039,7 @@ static void compileIfStatement(Compiler* compiler, bool else_if) {
|
|||||||
|
|
||||||
compileBlockBody(compiler, BLOCK_IF);
|
compileBlockBody(compiler, BLOCK_IF);
|
||||||
|
|
||||||
if (match(compiler, TK_ELSE)) {
|
if (match(compiler, TK_ELIF)) {
|
||||||
|
|
||||||
if (match(compiler, TK_IF)) { //< Compile 'else 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.
|
||||||
@ -3049,8 +3053,7 @@ static void compileIfStatement(Compiler* compiler, bool else_if) {
|
|||||||
|
|
||||||
patchJump(compiler, exit_jump);
|
patchJump(compiler, exit_jump);
|
||||||
|
|
||||||
} else { //< Compile 'else'.
|
} else if (match(compiler, TK_ELSE)) {
|
||||||
|
|
||||||
// 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.
|
||||||
@ -3058,15 +3061,14 @@ static void compileIfStatement(Compiler* compiler, bool else_if) {
|
|||||||
patchJump(compiler, ifpatch);
|
patchJump(compiler, ifpatch);
|
||||||
compileBlockBody(compiler, BLOCK_ELSE);
|
compileBlockBody(compiler, BLOCK_ELSE);
|
||||||
patchJump(compiler, exit_jump);
|
patchJump(compiler, exit_jump);
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
patchJump(compiler, ifpatch);
|
patchJump(compiler, ifpatch);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 'else if' will not consume the 'end' keyword as it'll be leaved to be
|
// elif will not consume the 'end' keyword as it'll be leaved to be consumed
|
||||||
// consumed by it's 'if'.
|
// by it's 'if'.
|
||||||
if (!else_if) {
|
if (!elif) {
|
||||||
skipNewLines(compiler);
|
skipNewLines(compiler);
|
||||||
consume(compiler, TK_END, "Expected 'end' after statement end.");
|
consume(compiler, TK_END, "Expected 'end' after statement end.");
|
||||||
}
|
}
|
||||||
|
@ -237,13 +237,6 @@ static inline bool _callBinaryOpMethod(PKVM* vm, Var self, Var other,
|
|||||||
/* CORE BUILTIN FUNCTIONS */
|
/* CORE BUILTIN FUNCTIONS */
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
|
||||||
DEF(coreTypeName,
|
|
||||||
"type_name(value:var) -> string\n"
|
|
||||||
"Returns the type name of the of the value.") {
|
|
||||||
|
|
||||||
RET(VAR_OBJ(newString(vm, varTypeName(ARG(1)))));
|
|
||||||
}
|
|
||||||
|
|
||||||
DEF(coreHelp,
|
DEF(coreHelp,
|
||||||
"help([fn:Closure]) -> null\n"
|
"help([fn:Closure]) -> null\n"
|
||||||
"It'll print the docstring the object and return.") {
|
"It'll print the docstring the object and return.") {
|
||||||
@ -545,7 +538,6 @@ static void initializeBuiltinFunctions(PKVM* vm) {
|
|||||||
initializeBuiltinFN(vm, &vm->builtins_funcs[vm->builtins_count++], name, \
|
initializeBuiltinFN(vm, &vm->builtins_funcs[vm->builtins_count++], name, \
|
||||||
(int)strlen(name), argc, fn, DOCSTRING(fn));
|
(int)strlen(name), argc, fn, DOCSTRING(fn));
|
||||||
// General functions.
|
// General functions.
|
||||||
INITIALIZE_BUILTIN_FN("type_name", coreTypeName, 1);
|
|
||||||
INITIALIZE_BUILTIN_FN("help", coreHelp, -1);
|
INITIALIZE_BUILTIN_FN("help", coreHelp, -1);
|
||||||
INITIALIZE_BUILTIN_FN("assert", coreAssert, -1);
|
INITIALIZE_BUILTIN_FN("assert", coreAssert, -1);
|
||||||
INITIALIZE_BUILTIN_FN("bin", coreBin, 1);
|
INITIALIZE_BUILTIN_FN("bin", coreBin, 1);
|
||||||
@ -642,31 +634,6 @@ DEF(stdLangDebugBreak,
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
DEF(stdLangWrite,
|
|
||||||
"write(...) -> null\n"
|
|
||||||
"Write function, just like print function but it wont put space between"
|
|
||||||
"args and write a new line at the end.") {
|
|
||||||
|
|
||||||
// If the host application doesn't provide any write function, discard the
|
|
||||||
// output.
|
|
||||||
if (vm->config.stdout_write == NULL) return;
|
|
||||||
|
|
||||||
String* str; //< Will be cleaned by garbage collector;
|
|
||||||
|
|
||||||
for (int i = 1; i <= ARGC; i++) {
|
|
||||||
Var arg = ARG(i);
|
|
||||||
// If it's already a string don't allocate a new string instead use it.
|
|
||||||
if (IS_OBJ_TYPE(arg, OBJ_STRING)) {
|
|
||||||
str = (String*)AS_OBJ(arg);
|
|
||||||
} else {
|
|
||||||
str = varToString(vm, ARG(1), false);
|
|
||||||
if (str == NULL) RET(VAR_NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
vm->config.stdout_write(vm, str->data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void initializeCoreModules(PKVM* vm) {
|
static void initializeCoreModules(PKVM* vm) {
|
||||||
#define MODULE_ADD_FN(module, name, fn, argc) \
|
#define MODULE_ADD_FN(module, name, fn, argc) \
|
||||||
moduleAddFunctionInternal(vm, module, name, fn, argc, DOCSTRING(fn))
|
moduleAddFunctionInternal(vm, module, name, fn, argc, DOCSTRING(fn))
|
||||||
@ -680,7 +647,6 @@ static void initializeCoreModules(PKVM* vm) {
|
|||||||
NEW_MODULE(lang, "lang");
|
NEW_MODULE(lang, "lang");
|
||||||
MODULE_ADD_FN(lang, "gc", stdLangGC, 0);
|
MODULE_ADD_FN(lang, "gc", stdLangGC, 0);
|
||||||
MODULE_ADD_FN(lang, "disas", stdLangDisas, 1);
|
MODULE_ADD_FN(lang, "disas", stdLangDisas, 1);
|
||||||
MODULE_ADD_FN(lang, "write", stdLangWrite, -1);
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
MODULE_ADD_FN(lang, "debug_break", stdLangDebugBreak, 0);
|
MODULE_ADD_FN(lang, "debug_break", stdLangDebugBreak, 0);
|
||||||
#endif
|
#endif
|
||||||
@ -764,6 +730,18 @@ static void _ctorFiber(PKVM* vm) {
|
|||||||
|
|
||||||
#define SELF (vm->fiber->self)
|
#define SELF (vm->fiber->self)
|
||||||
|
|
||||||
|
DEF(_objTypeName,
|
||||||
|
"Object.typename() -> String\n"
|
||||||
|
"Returns the type name of the object.") {
|
||||||
|
RET(VAR_OBJ(newString(vm, varTypeName(SELF))));
|
||||||
|
}
|
||||||
|
|
||||||
|
DEF(_objRepr,
|
||||||
|
"Object.repr() -> String\n"
|
||||||
|
"Returns the repr string of the object.") {
|
||||||
|
RET(VAR_OBJ(varToString(vm, SELF, true)));
|
||||||
|
}
|
||||||
|
|
||||||
DEF(_numberTimes,
|
DEF(_numberTimes,
|
||||||
"Number.times(f:fn)\n"
|
"Number.times(f:fn)\n"
|
||||||
"Iterate the function [f] n times. Here n is the integral value of the "
|
"Iterate the function [f] n times. Here n is the integral value of the "
|
||||||
@ -1166,6 +1144,9 @@ static void initializePrimitiveClasses(PKVM* vm) {
|
|||||||
} while (false)
|
} while (false)
|
||||||
|
|
||||||
// TODO: write docs.
|
// TODO: write docs.
|
||||||
|
ADD_METHOD(PK_OBJECT, "typename", _objTypeName, 0);
|
||||||
|
ADD_METHOD(PK_OBJECT, "repr", _objRepr, 0);
|
||||||
|
|
||||||
ADD_METHOD(PK_NUMBER, "times", _numberTimes, 1);
|
ADD_METHOD(PK_NUMBER, "times", _numberTimes, 1);
|
||||||
ADD_METHOD(PK_NUMBER, "isint", _numberIsint, 0);
|
ADD_METHOD(PK_NUMBER, "isint", _numberIsint, 0);
|
||||||
ADD_METHOD(PK_NUMBER, "isbyte", _numberIsbyte, 0);
|
ADD_METHOD(PK_NUMBER, "isbyte", _numberIsbyte, 0);
|
||||||
@ -1465,6 +1446,26 @@ Var varSubtract(PKVM* vm, Var v1, Var v2, bool inplace) {
|
|||||||
Var varMultiply(PKVM* vm, Var v1, Var v2, bool inplace) {
|
Var varMultiply(PKVM* vm, Var v1, Var v2, bool inplace) {
|
||||||
CHECK_NUMERIC_OP(*);
|
CHECK_NUMERIC_OP(*);
|
||||||
CHECK_INST_BINARY_OP("*");
|
CHECK_INST_BINARY_OP("*");
|
||||||
|
|
||||||
|
if (IS_OBJ_TYPE(v1, OBJ_STRING)) {
|
||||||
|
String* left = (String*) AS_OBJ(v1);
|
||||||
|
int64_t right;
|
||||||
|
if (isInteger(v2, &right)) {
|
||||||
|
if (left->length == 0) return VAR_OBJ(left);
|
||||||
|
if (right == 0) return VAR_OBJ(newString(vm, ""));
|
||||||
|
|
||||||
|
String* str = newStringLength(vm, "", left->length * (uint32_t) right);
|
||||||
|
char* buff = str->data;
|
||||||
|
for (int i = 0; i < (int) right; i++) {
|
||||||
|
memcpy(buff, left->data, left->length);
|
||||||
|
buff += left->length;
|
||||||
|
}
|
||||||
|
ASSERT(buff == str->data + str->length, OOPS);
|
||||||
|
str->hash = utilHashString(str->data);
|
||||||
|
return VAR_OBJ(str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
UNSUPPORTED_BINARY_OP("*");
|
UNSUPPORTED_BINARY_OP("*");
|
||||||
return VAR_NULL;
|
return VAR_NULL;
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,8 @@
|
|||||||
|
|
||||||
// This file contains all the pocketlang public function implementations.
|
// This file contains all the pocketlang public function implementations.
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
#ifndef PK_AMALGAMATED
|
#ifndef PK_AMALGAMATED
|
||||||
#include <pocketlang.h>
|
#include <pocketlang.h>
|
||||||
#include "core.h"
|
#include "core.h"
|
||||||
@ -78,7 +80,7 @@ static void stdoutWrite(PKVM* vm, const char* text);
|
|||||||
static char* stdinRead(PKVM* vm);
|
static char* stdinRead(PKVM* vm);
|
||||||
static char* loadScript(PKVM* vm, const char* path);
|
static char* loadScript(PKVM* vm, const char* path);
|
||||||
|
|
||||||
PK_PUBLIC void* pkRealloc(PKVM* vm, void* ptr, size_t size) {
|
void* pkRealloc(PKVM* vm, void* ptr, size_t size) {
|
||||||
ASSERT(vm->config.realloc_fn != NULL, "PKVM's allocator was NULL.");
|
ASSERT(vm->config.realloc_fn != NULL, "PKVM's allocator was NULL.");
|
||||||
#if TRACE_MEMORY
|
#if TRACE_MEMORY
|
||||||
void* newptr = vm->config.realloc_fn(ptr, size, vm->config.user_data);
|
void* newptr = vm->config.realloc_fn(ptr, size, vm->config.user_data);
|
||||||
@ -553,7 +555,7 @@ bool pkCheckArgcRange(PKVM* vm, int argc, int min, int max) {
|
|||||||
|
|
||||||
// FIXME: If the user needs just the boolean value of the object, they should
|
// FIXME: If the user needs just the boolean value of the object, they should
|
||||||
// use pkGetSlotBool().
|
// use pkGetSlotBool().
|
||||||
PK_PUBLIC bool pkValidateSlotBool(PKVM* vm, int slot, bool* value) {
|
bool pkValidateSlotBool(PKVM* vm, int slot, bool* value) {
|
||||||
CHECK_FIBER_EXISTS(vm);
|
CHECK_FIBER_EXISTS(vm);
|
||||||
VALIDATE_SLOT_INDEX(slot);
|
VALIDATE_SLOT_INDEX(slot);
|
||||||
|
|
||||||
@ -567,7 +569,7 @@ PK_PUBLIC bool pkValidateSlotBool(PKVM* vm, int slot, bool* value) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
PK_PUBLIC bool pkValidateSlotNumber(PKVM* vm, int slot, double* value) {
|
bool pkValidateSlotNumber(PKVM* vm, int slot, double* value) {
|
||||||
CHECK_FIBER_EXISTS(vm);
|
CHECK_FIBER_EXISTS(vm);
|
||||||
VALIDATE_SLOT_INDEX(slot);
|
VALIDATE_SLOT_INDEX(slot);
|
||||||
|
|
||||||
@ -581,7 +583,23 @@ PK_PUBLIC bool pkValidateSlotNumber(PKVM* vm, int slot, double* value) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
PK_PUBLIC bool pkValidateSlotString(PKVM* vm, int slot, const char** value,
|
bool pkValidateSlotInteger(PKVM* vm, int slot, int32_t* value) {
|
||||||
|
CHECK_FIBER_EXISTS(vm);
|
||||||
|
VALIDATE_SLOT_INDEX(slot);
|
||||||
|
|
||||||
|
double n;
|
||||||
|
if (!pkValidateSlotNumber(vm, slot, &n)) return false;
|
||||||
|
|
||||||
|
if (floor(n) != n) {
|
||||||
|
VM_SET_ERROR(vm, newString(vm, "Expected an integer got float."));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value) *value = (int32_t) n;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool pkValidateSlotString(PKVM* vm, int slot, const char** value,
|
||||||
uint32_t* length) {
|
uint32_t* length) {
|
||||||
CHECK_FIBER_EXISTS(vm);
|
CHECK_FIBER_EXISTS(vm);
|
||||||
VALIDATE_SLOT_INDEX(slot);
|
VALIDATE_SLOT_INDEX(slot);
|
||||||
@ -608,7 +626,7 @@ bool pkValidateSlotType(PKVM* vm, int slot, PkVarType type) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
PK_PUBLIC bool pkValidateSlotInstanceOf(PKVM* vm, int slot, int cls) {
|
bool pkValidateSlotInstanceOf(PKVM* vm, int slot, int cls) {
|
||||||
CHECK_FIBER_EXISTS(vm);
|
CHECK_FIBER_EXISTS(vm);
|
||||||
VALIDATE_SLOT_INDEX(slot);
|
VALIDATE_SLOT_INDEX(slot);
|
||||||
VALIDATE_SLOT_INDEX(cls);
|
VALIDATE_SLOT_INDEX(cls);
|
||||||
@ -719,7 +737,7 @@ void pkSetSlotString(PKVM* vm, int index, const char* value) {
|
|||||||
SET_SLOT(index, VAR_OBJ(newString(vm, value)));
|
SET_SLOT(index, VAR_OBJ(newString(vm, value)));
|
||||||
}
|
}
|
||||||
|
|
||||||
PK_PUBLIC void pkSetSlotStringLength(PKVM* vm, int index,
|
void pkSetSlotStringLength(PKVM* vm, int index,
|
||||||
const char* value, uint32_t length) {
|
const char* value, uint32_t length) {
|
||||||
CHECK_FIBER_EXISTS(vm);
|
CHECK_FIBER_EXISTS(vm);
|
||||||
VALIDATE_SLOT_INDEX(index);
|
VALIDATE_SLOT_INDEX(index);
|
||||||
@ -751,7 +769,7 @@ bool pkSetAttribute(PKVM* vm, int instance, const char* name, int value) {
|
|||||||
return !VM_HAS_ERROR(vm);
|
return !VM_HAS_ERROR(vm);
|
||||||
}
|
}
|
||||||
|
|
||||||
PK_PUBLIC bool pkGetAttribute(PKVM* vm, int instance, const char* name,
|
bool pkGetAttribute(PKVM* vm, int instance, const char* name,
|
||||||
int index) {
|
int index) {
|
||||||
CHECK_FIBER_EXISTS(vm);
|
CHECK_FIBER_EXISTS(vm);
|
||||||
CHECK_ARG_NULL(name);
|
CHECK_ARG_NULL(name);
|
||||||
@ -830,7 +848,7 @@ bool pkCallFunction(PKVM* vm, int fn, int argc, int argv, int ret) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
PK_PUBLIC bool pkCallMethod(PKVM* vm, int instance, const char* method,
|
bool pkCallMethod(PKVM* vm, int instance, const char* method,
|
||||||
int argc, int argv, int ret) {
|
int argc, int argv, int ret) {
|
||||||
CHECK_FIBER_EXISTS(vm);
|
CHECK_FIBER_EXISTS(vm);
|
||||||
CHECK_ARG_NULL(method);
|
CHECK_ARG_NULL(method);
|
||||||
@ -928,7 +946,7 @@ static char* stdinRead(PKVM* vm) {
|
|||||||
pkByteBufferInit(&buff);
|
pkByteBufferInit(&buff);
|
||||||
char c;
|
char c;
|
||||||
do {
|
do {
|
||||||
c = (char)fgetc(stdin);
|
c = (char) fgetc(stdin);
|
||||||
if (c == '\n') break;
|
if (c == '\n') break;
|
||||||
pkByteBufferWrite(&buff, vm, (uint8_t)c);
|
pkByteBufferWrite(&buff, vm, (uint8_t)c);
|
||||||
} while (c != EOF);
|
} while (c != EOF);
|
||||||
@ -936,6 +954,7 @@ static char* stdinRead(PKVM* vm) {
|
|||||||
|
|
||||||
char* str = pkRealloc(vm, NULL, buff.count);
|
char* str = pkRealloc(vm, NULL, buff.count);
|
||||||
memcpy(str, buff.data, buff.count);
|
memcpy(str, buff.data, buff.count);
|
||||||
|
pkByteBufferClear(&buff, vm);
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,6 +98,11 @@ typedef void (*pkWriteFn) (PKVM* vm, const char* text);
|
|||||||
// string.
|
// string.
|
||||||
typedef char* (*pkReadFn) (PKVM* vm);
|
typedef char* (*pkReadFn) (PKVM* vm);
|
||||||
|
|
||||||
|
// A generic function thiat could be used by the PKVM to signal something to
|
||||||
|
// the host application. The first argument is depend on the callback it's
|
||||||
|
// registered.
|
||||||
|
typedef void (*pkSignalFn) (void*);
|
||||||
|
|
||||||
// Load and return the script. Called by the compiler to fetch initial source
|
// Load and return the script. Called by the compiler to fetch initial source
|
||||||
// code and source for import statements. Return NULL to indicate failure to
|
// code and source for import statements. Return NULL to indicate failure to
|
||||||
// load. Otherwise the string **must** be allocated with pkRealloc() and
|
// load. Otherwise the string **must** be allocated with pkRealloc() and
|
||||||
@ -170,10 +175,12 @@ struct PkConfiguration {
|
|||||||
// pointer is NULL it defaults to the VM's realloc(), free() wrappers.
|
// pointer is NULL it defaults to the VM's realloc(), free() wrappers.
|
||||||
pkReallocFn realloc_fn;
|
pkReallocFn realloc_fn;
|
||||||
|
|
||||||
|
// I/O callbacks.
|
||||||
pkWriteFn stderr_write;
|
pkWriteFn stderr_write;
|
||||||
pkWriteFn stdout_write;
|
pkWriteFn stdout_write;
|
||||||
pkReadFn stdin_read;
|
pkReadFn stdin_read;
|
||||||
|
|
||||||
|
// Import system callbacks.
|
||||||
pkResolvePathFn resolve_path_fn;
|
pkResolvePathFn resolve_path_fn;
|
||||||
pkLoadScriptFn load_script_fn;
|
pkLoadScriptFn load_script_fn;
|
||||||
|
|
||||||
@ -291,6 +298,10 @@ PK_PUBLIC bool pkValidateSlotBool(PKVM* vm, int slot, bool* value);
|
|||||||
// if not set a runtime error.
|
// if not set a runtime error.
|
||||||
PK_PUBLIC bool pkValidateSlotNumber(PKVM* vm, int slot, double* value);
|
PK_PUBLIC bool pkValidateSlotNumber(PKVM* vm, int slot, double* value);
|
||||||
|
|
||||||
|
// Helper function to check if the argument at the [slot] is an a whold number
|
||||||
|
// and if not set a runtime error.
|
||||||
|
PK_PUBLIC bool pkValidateSlotInteger(PKVM* vm, int slot, int32_t* value);
|
||||||
|
|
||||||
// Helper function to check if the argument at the [slot] slot is String and
|
// Helper function to check if the argument at the [slot] slot is String and
|
||||||
// if not set a runtime error.
|
// if not set a runtime error.
|
||||||
PK_PUBLIC bool pkValidateSlotString(PKVM* vm, int slot,
|
PK_PUBLIC bool pkValidateSlotString(PKVM* vm, int slot,
|
||||||
|
@ -4,10 +4,56 @@
|
|||||||
* Distributed Under The MIT License
|
* Distributed Under The MIT License
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
#ifndef PK_AMALGAMATED
|
#ifndef PK_AMALGAMATED
|
||||||
#include "libs.h"
|
#include "libs.h"
|
||||||
|
#include "../core/value.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
DEF(_ioWrite,
|
||||||
|
"io.write(stream:var, bytes:String) -> null\n"
|
||||||
|
"Warning: the function is subjected to be changed anytime soon.\n"
|
||||||
|
"Write [bytes] string to the stream. stream should be any of io.stdin, "
|
||||||
|
"io.stdout, io.stderr.") {
|
||||||
|
|
||||||
|
double stream;
|
||||||
|
if (!pkValidateSlotNumber(vm, 1, &stream)) return;
|
||||||
|
|
||||||
|
if (stream != 0 && stream != 1 && stream != 2) {
|
||||||
|
pkSetRuntimeErrorFmt(vm, "Invalid stream (%g). Only use any of io.stdin, "
|
||||||
|
"io.stdout, io.stderr.", stream);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* bytes;
|
||||||
|
uint32_t length;
|
||||||
|
if (!pkValidateSlotString(vm, 2, &bytes, &length)) return;
|
||||||
|
|
||||||
|
switch ((int) stream) {
|
||||||
|
case 0:
|
||||||
|
pkSetRuntimeError(vm, "Cannot write to stdin.");
|
||||||
|
return;
|
||||||
|
|
||||||
|
// TODO: If the string contain null bytes it won't print anything after
|
||||||
|
// that, not sure if that needs to be fixed.
|
||||||
|
case 1:
|
||||||
|
fprintf(stdout, "%s", bytes);
|
||||||
|
return;
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
fprintf(stderr, "%s", bytes);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DEF(_ioFlush,
|
||||||
|
"io.flush() -> null\n"
|
||||||
|
"Warning: the function is subjected to be changed anytime soon.\n"
|
||||||
|
"Flush stdout buffer.\n") {
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
/* FILE CLASS */
|
/* FILE CLASS */
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
@ -22,13 +68,26 @@
|
|||||||
// 'a+' | write to end | create new |
|
// 'a+' | write to end | create new |
|
||||||
typedef enum {
|
typedef enum {
|
||||||
FMODE_NONE = 0,
|
FMODE_NONE = 0,
|
||||||
|
|
||||||
FMODE_READ = (1 << 0),
|
FMODE_READ = (1 << 0),
|
||||||
FMODE_WRITE = (1 << 1),
|
FMODE_WRITE = (1 << 1),
|
||||||
FMODE_APPEND = (1 << 2),
|
FMODE_APPEND = (1 << 2),
|
||||||
|
|
||||||
_FMODE_EXT = (1 << 3),
|
_FMODE_EXT = (1 << 3),
|
||||||
|
_FMODE_BIN = (1 << 4),
|
||||||
|
|
||||||
FMODE_READ_EXT = (_FMODE_EXT | FMODE_READ),
|
FMODE_READ_EXT = (_FMODE_EXT | FMODE_READ),
|
||||||
FMODE_WRITE_EXT = (_FMODE_EXT | FMODE_WRITE),
|
FMODE_WRITE_EXT = (_FMODE_EXT | FMODE_WRITE),
|
||||||
FMODE_APPEND_EXT = (_FMODE_EXT | FMODE_APPEND),
|
FMODE_APPEND_EXT = (_FMODE_EXT | FMODE_APPEND),
|
||||||
|
|
||||||
|
FMODE_READ_BIN = (_FMODE_BIN | FMODE_READ),
|
||||||
|
FMODE_WRITE_BIN = (_FMODE_BIN | FMODE_READ),
|
||||||
|
FMODE_APPEND_BIN = (_FMODE_BIN | FMODE_READ),
|
||||||
|
|
||||||
|
FMODE_READ_BIN_EXT = (_FMODE_BIN | FMODE_READ_EXT),
|
||||||
|
FMODE_WRITE_BIN_EXT = (_FMODE_BIN | FMODE_WRITE_EXT),
|
||||||
|
FMODE_APPEND_BIN_EXT = (_FMODE_BIN | FMODE_APPEND_EXT),
|
||||||
|
|
||||||
} FileAccessMode;
|
} FileAccessMode;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@ -49,9 +108,15 @@ void* _newFile(PKVM* vm) {
|
|||||||
void _deleteFile(PKVM* vm, void* ptr) {
|
void _deleteFile(PKVM* vm, void* ptr) {
|
||||||
File* file = (File*)ptr;
|
File* file = (File*)ptr;
|
||||||
if (!file->closed) {
|
if (!file->closed) {
|
||||||
|
ASSERT(file->fp != NULL, OOPS);
|
||||||
if (fclose(file->fp) != 0) { /* TODO: error! */ }
|
if (fclose(file->fp) != 0) { /* TODO: error! */ }
|
||||||
file->closed = true;
|
file->closed = true;
|
||||||
|
file->fp = NULL;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
ASSERT(file->fp == NULL, OOPS);
|
||||||
}
|
}
|
||||||
|
|
||||||
pkRealloc(vm, file, 0);
|
pkRealloc(vm, file, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,15 +138,24 @@ DEF(_fileOpen, "") {
|
|||||||
if (argc == 2) {
|
if (argc == 2) {
|
||||||
if (!pkValidateSlotString(vm, 2, &mode_str, NULL)) return;
|
if (!pkValidateSlotString(vm, 2, &mode_str, NULL)) return;
|
||||||
|
|
||||||
// Check if the mode string is valid, and update the mode value.
|
// TODO: I should properly parser this.
|
||||||
do {
|
do {
|
||||||
if (strcmp(mode_str, "r") == 0) { mode = FMODE_READ; break; }
|
if (strcmp(mode_str, "r") == 0) { mode = FMODE_READ; break; }
|
||||||
if (strcmp(mode_str, "w") == 0) { mode = FMODE_WRITE; break; }
|
if (strcmp(mode_str, "w") == 0) { mode = FMODE_WRITE; break; }
|
||||||
if (strcmp(mode_str, "a") == 0) { mode = FMODE_APPEND; break; }
|
if (strcmp(mode_str, "a") == 0) { mode = FMODE_APPEND; break; }
|
||||||
|
|
||||||
if (strcmp(mode_str, "r+") == 0) { mode = FMODE_READ_EXT; break; }
|
if (strcmp(mode_str, "r+") == 0) { mode = FMODE_READ_EXT; break; }
|
||||||
if (strcmp(mode_str, "w+") == 0) { mode = FMODE_WRITE_EXT; break; }
|
if (strcmp(mode_str, "w+") == 0) { mode = FMODE_WRITE_EXT; break; }
|
||||||
if (strcmp(mode_str, "a+") == 0) { mode = FMODE_APPEND_EXT; break; }
|
if (strcmp(mode_str, "a+") == 0) { mode = FMODE_APPEND_EXT; break; }
|
||||||
|
|
||||||
|
if (strcmp(mode_str, "rb") == 0) { mode = FMODE_READ_BIN; break; }
|
||||||
|
if (strcmp(mode_str, "wb") == 0) { mode = FMODE_WRITE_BIN; break; }
|
||||||
|
if (strcmp(mode_str, "ab") == 0) { mode = FMODE_APPEND_BIN; break; }
|
||||||
|
|
||||||
|
if (strcmp(mode_str, "rb+") == 0) { mode = FMODE_READ_BIN_EXT; break; }
|
||||||
|
if (strcmp(mode_str, "wb+") == 0) { mode = FMODE_WRITE_BIN_EXT; break; }
|
||||||
|
if (strcmp(mode_str, "ab+") == 0) { mode = FMODE_APPEND_BIN_EXT; break; }
|
||||||
|
|
||||||
// TODO: (fmt, ...) va_arg for runtime error public api.
|
// TODO: (fmt, ...) va_arg for runtime error public api.
|
||||||
// If we reached here, that means it's an invalid mode string.
|
// If we reached here, that means it's an invalid mode string.
|
||||||
pkSetRuntimeError(vm, "Invalid mode string.");
|
pkSetRuntimeError(vm, "Invalid mode string.");
|
||||||
@ -89,14 +163,10 @@ DEF(_fileOpen, "") {
|
|||||||
} while (false);
|
} while (false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// This TODO is just a blockade from running the bellow code, complete the
|
|
||||||
// native interface and test before removing it.
|
|
||||||
TODO;
|
|
||||||
|
|
||||||
FILE* fp = fopen(path, mode_str);
|
FILE* fp = fopen(path, mode_str);
|
||||||
|
|
||||||
if (fp != NULL) {
|
if (fp != NULL) {
|
||||||
File* self = (File*)pkGetSelf(vm);
|
File* self = (File*) pkGetSelf(vm);
|
||||||
self->fp = fp;
|
self->fp = fp;
|
||||||
self->mode = mode;
|
self->mode = mode;
|
||||||
self->closed = false;
|
self->closed = false;
|
||||||
@ -107,36 +177,145 @@ DEF(_fileOpen, "") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
DEF(_fileRead, "") {
|
DEF(_fileRead, "") {
|
||||||
// This TODO is just a blockade from running the bellow code, complete the
|
|
||||||
// native interface and test before removing it.
|
|
||||||
TODO;
|
|
||||||
|
|
||||||
File* file = (File*)pkGetSelf(vm);
|
int argc = pkGetArgc(vm);
|
||||||
|
if (!pkCheckArgcRange(vm, argc, 0, 1)) return;
|
||||||
|
|
||||||
|
// If count == -1, that means read all bytes.
|
||||||
|
long count = -1;
|
||||||
|
|
||||||
|
if (argc == 1) {
|
||||||
|
// Not using pkValidateInteger since it's supports 32 bit int range.
|
||||||
|
// but we need a long.
|
||||||
|
double count_;
|
||||||
|
if (!pkValidateSlotNumber(vm, 1, &count_)) return;
|
||||||
|
if (floor(count_) != count_) {
|
||||||
|
pkSetRuntimeError(vm, "Expected an integer.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (count_ < 0 && count_ != -1) {
|
||||||
|
pkSetRuntimeError(vm, "Read bytes count should be either > 0 or == -1.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
count = (long) count_;
|
||||||
|
}
|
||||||
|
|
||||||
|
File* file = (File*) pkGetSelf(vm);
|
||||||
|
|
||||||
if (file->closed) {
|
if (file->closed) {
|
||||||
pkSetRuntimeError(vm, "Cannot read from a closed file.");
|
pkSetRuntimeError(vm, "Cannot read from a closed file.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((file->mode != FMODE_READ) && ((_FMODE_EXT & file->mode) == 0)) {
|
if (!(file->mode & FMODE_READ) && !(_FMODE_EXT & file->mode)) {
|
||||||
pkSetRuntimeError(vm, "File is not readable.");
|
pkSetRuntimeError(vm, "File is not readable.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: this is temporary.
|
if (count == -1) {
|
||||||
|
// Get the source length. In windows the ftell will includes the cariage
|
||||||
|
// return when using ftell with fseek. But that's not an issue since
|
||||||
|
// we'll be allocating more memory than needed for fread().
|
||||||
|
long current = ftell(file->fp);
|
||||||
|
fseek(file->fp, 0, SEEK_END);
|
||||||
|
count = ftell(file->fp);
|
||||||
|
fseek(file->fp, current, SEEK_SET);
|
||||||
|
}
|
||||||
|
|
||||||
char buff[2048];
|
// Allocate string + 1 for the NULL terminator.
|
||||||
size_t read = fread((void*)buff, sizeof(char), sizeof(buff), file->fp);
|
char* buff = pkRealloc(vm, NULL, (size_t) count + 1);
|
||||||
(void) read;
|
ASSERT(buff != NULL, "pkRealloc failed.");
|
||||||
pkSetSlotString(vm, 0, (const char*)buff);
|
|
||||||
|
clearerr(file->fp);
|
||||||
|
size_t read = fread(buff, sizeof(char), count, file->fp);
|
||||||
|
|
||||||
|
if (ferror(file->fp)) {
|
||||||
|
pkSetRuntimeErrorFmt(vm, "An error occured at C.fread() - '%s'.",
|
||||||
|
strerror(errno));
|
||||||
|
goto L_done;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_read_failed = read > count;
|
||||||
|
if (!is_read_failed) buff[read] = '\0';
|
||||||
|
|
||||||
|
// If EOF is already reached it won't read anymore bytes.
|
||||||
|
if (read == 0) {
|
||||||
|
pkSetSlotStringLength(vm, 0, "", 0);
|
||||||
|
goto L_done;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_read_failed) {
|
||||||
|
pkSetRuntimeError(vm, "C.fread() failed.");
|
||||||
|
goto L_done;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: maybe I should check if the [read] length is larger for uint32_t.
|
||||||
|
pkSetSlotStringLength(vm, 0, buff, (uint32_t) read);
|
||||||
|
goto L_done;
|
||||||
|
|
||||||
|
L_done:
|
||||||
|
pkRealloc(vm, buff, 0);
|
||||||
|
return;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note that fgetline is not standard in older version of C. so we're defining
|
||||||
|
// something similler.
|
||||||
|
DEF(_fileGetLine, "") {
|
||||||
|
File* file = (File*) pkGetSelf(vm);
|
||||||
|
|
||||||
|
if (file->closed) {
|
||||||
|
pkSetRuntimeError(vm, "Cannot read from a closed file.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(file->mode & FMODE_READ) && !(_FMODE_EXT & file->mode)) {
|
||||||
|
pkSetRuntimeError(vm, "File is not readable.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (file->mode & _FMODE_BIN) {
|
||||||
|
pkSetRuntimeError(vm, "Cannot getline binary files.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME:
|
||||||
|
// I'm not sure this is an efficient method to read a line using fgetc
|
||||||
|
// and a dynamic buffer. Consider fgets() or maybe find '\n' in file
|
||||||
|
// and use pkRealloc() with size using ftell() or something.
|
||||||
|
|
||||||
|
pkByteBuffer buff;
|
||||||
|
pkByteBufferInit(&buff);
|
||||||
|
char c;
|
||||||
|
do {
|
||||||
|
c = (char) fgetc(file->fp);
|
||||||
|
|
||||||
|
// End of file or error.
|
||||||
|
if (c == EOF) {
|
||||||
|
if (ferror(file->fp)) {
|
||||||
|
pkSetRuntimeErrorFmt(vm, "Error while reading line - '%s'.",
|
||||||
|
strerror(errno));
|
||||||
|
goto L_done;
|
||||||
|
}
|
||||||
|
break; // EOF is reached.
|
||||||
|
}
|
||||||
|
|
||||||
|
pkByteBufferWrite(&buff, vm, (uint8_t) c);
|
||||||
|
if (c == '\n') break;
|
||||||
|
|
||||||
|
} while (true);
|
||||||
|
|
||||||
|
// A null byte '\0' will be added by pocketlang.
|
||||||
|
pkSetSlotStringLength(vm, 0, buff.data, buff.count);
|
||||||
|
|
||||||
|
L_done:
|
||||||
|
pkByteBufferClear(&buff, vm);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
DEF(_fileWrite, "") {
|
DEF(_fileWrite, "") {
|
||||||
// This TODO is just a blockade from running the bellow code, complete the
|
|
||||||
// native interface and test before removing it.
|
|
||||||
TODO;
|
|
||||||
|
|
||||||
File* file = (File*)pkGetSelf(vm);
|
File* file = (File*) pkGetSelf(vm);
|
||||||
const char* text; uint32_t length;
|
const char* text; uint32_t length;
|
||||||
if (!pkValidateSlotString(vm, 1, &text, &length)) return;
|
if (!pkValidateSlotString(vm, 1, &text, &length)) return;
|
||||||
|
|
||||||
@ -145,22 +324,28 @@ DEF(_fileWrite, "") {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((file->mode != FMODE_WRITE) && ((_FMODE_EXT & file->mode) == 0)) {
|
if (!(file->mode & FMODE_WRITE)
|
||||||
|
&& !(file->mode & FMODE_APPEND)
|
||||||
|
&& !(file->mode & _FMODE_EXT)) {
|
||||||
pkSetRuntimeError(vm, "File is not writable.");
|
pkSetRuntimeError(vm, "File is not writable.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
fwrite(text, sizeof(char), (size_t)length, file->fp);
|
clearerr(file->fp);
|
||||||
|
fwrite(text, sizeof(char), (size_t) length, file->fp);
|
||||||
|
if (ferror(file->fp)) {
|
||||||
|
pkSetRuntimeErrorFmt(vm, "An error occureed at C.fwrite() - '%s'.",
|
||||||
|
strerror(errno));
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DEF(_fileClose, "") {
|
DEF(_fileClose, "") {
|
||||||
// This TODO is just a blockade from running the bellow code, complete the
|
|
||||||
// native interface and test before removing it.
|
|
||||||
TODO;
|
|
||||||
|
|
||||||
File* file = (File*)pkGetSelf(vm);
|
File* file = (File*) pkGetSelf(vm);
|
||||||
|
|
||||||
if (file->closed) {
|
if (file->closed) {
|
||||||
|
ASSERT(file->fp == NULL, OOPS);
|
||||||
pkSetRuntimeError(vm, "File already closed.");
|
pkSetRuntimeError(vm, "File already closed.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -168,17 +353,79 @@ DEF(_fileClose, "") {
|
|||||||
if (fclose(file->fp) != 0) {
|
if (fclose(file->fp) != 0) {
|
||||||
pkSetRuntimeError(vm, "fclose() failed!.");
|
pkSetRuntimeError(vm, "fclose() failed!.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
file->fp = NULL;
|
||||||
file->closed = true;
|
file->closed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DEF(_fileSeek,
|
||||||
|
"io.File.seek(offset:int, whence:int) -> null\n"
|
||||||
|
"") {
|
||||||
|
|
||||||
|
int argc = pkGetArgc(vm);
|
||||||
|
if (!pkCheckArgcRange(vm, argc, 1, 2)) return;
|
||||||
|
|
||||||
|
int32_t offset = 0, whence = 0;
|
||||||
|
if (!pkValidateSlotInteger(vm, 1, &offset)) return;
|
||||||
|
|
||||||
|
if (argc == 2) {
|
||||||
|
if (!pkValidateSlotInteger(vm, 2, &whence)) return;
|
||||||
|
if (whence < 0 || whence > 2) {
|
||||||
|
pkSetRuntimeErrorFmt(vm, "Invalid whence value (%i).", whence);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
File* file = (File*) pkGetSelf(vm);
|
||||||
|
|
||||||
|
if (file->closed) {
|
||||||
|
pkSetRuntimeError(vm, "Cannot seek from a closed file.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fseek(file->fp, offset, whence) != 0) {
|
||||||
|
pkSetRuntimeErrorFmt(vm, "An error occureed at C.fseek() - '%s'.",
|
||||||
|
strerror(errno));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DEF(_fileTell, "") {
|
||||||
|
File* file = (File*) pkGetSelf(vm);
|
||||||
|
|
||||||
|
if (file->closed) {
|
||||||
|
pkSetRuntimeError(vm, "Cannot tell from a closed file.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// C.ftell() doesn't "throw" any error right?
|
||||||
|
pkSetSlotNumber(vm, 0, (double) ftell(file->fp));
|
||||||
|
}
|
||||||
|
|
||||||
void registerModuleIO(PKVM* vm) {
|
void registerModuleIO(PKVM* vm) {
|
||||||
|
|
||||||
PkHandle* io = pkNewModule(vm, "io");
|
PkHandle* io = pkNewModule(vm, "io");
|
||||||
|
|
||||||
|
pkReserveSlots(vm, 2);
|
||||||
|
pkSetSlotHandle(vm, 0, io); // slot[0] = io
|
||||||
|
pkSetSlotNumber(vm, 1, 0); // slot[1] = 0
|
||||||
|
pkSetAttribute(vm, 0, "stdin", 1); // slot[0].stdin = slot[1]
|
||||||
|
pkSetSlotNumber(vm, 1, 1); // slot[1] = 1
|
||||||
|
pkSetAttribute(vm, 0, "stdout", 1); // slot[0].stdout = slot[1]
|
||||||
|
pkSetSlotNumber(vm, 1, 2); // slot[1] = 2
|
||||||
|
pkSetAttribute(vm, 0, "stderr", 1); // slot[0].stderr = slot[1]
|
||||||
|
|
||||||
|
pkModuleAddFunction(vm, io, "write", _ioWrite, 2);
|
||||||
|
pkModuleAddFunction(vm, io, "flush", _ioFlush, 0);
|
||||||
|
|
||||||
PkHandle* cls_file = pkNewClass(vm, "File", NULL, io, _newFile, _deleteFile);
|
PkHandle* cls_file = pkNewClass(vm, "File", NULL, io, _newFile, _deleteFile);
|
||||||
pkClassAddMethod(vm, cls_file, "open", _fileOpen, -1);
|
pkClassAddMethod(vm, cls_file, "open", _fileOpen, -1);
|
||||||
pkClassAddMethod(vm, cls_file, "read", _fileRead, 0);
|
pkClassAddMethod(vm, cls_file, "read", _fileRead, -1);
|
||||||
pkClassAddMethod(vm, cls_file, "write", _fileWrite, 1);
|
pkClassAddMethod(vm, cls_file, "write", _fileWrite, 1);
|
||||||
|
pkClassAddMethod(vm, cls_file, "getline", _fileGetLine, 0);
|
||||||
pkClassAddMethod(vm, cls_file, "close", _fileClose, 0);
|
pkClassAddMethod(vm, cls_file, "close", _fileClose, 0);
|
||||||
|
pkClassAddMethod(vm, cls_file, "seek", _fileSeek, -1);
|
||||||
|
pkClassAddMethod(vm, cls_file, "tell", _fileTell, 0);
|
||||||
pkReleaseHandle(vm, cls_file);
|
pkReleaseHandle(vm, cls_file);
|
||||||
|
|
||||||
pkRegisterModule(vm, io);
|
pkRegisterModule(vm, io);
|
||||||
|
@ -36,6 +36,23 @@ void _bytebuffReserve(PKVM* vm) {
|
|||||||
pkByteBufferReserve(self, vm, (size_t) size);
|
pkByteBufferReserve(self, vm, (size_t) size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// buff.fill(data, count)
|
||||||
|
void _bytebuffFill(PKVM* vm) {
|
||||||
|
uint32_t n;
|
||||||
|
if (!pkValidateSlotInteger(vm, 1, &n)) return;
|
||||||
|
if (n < 0x00 || n > 0xff) {
|
||||||
|
pkSetRuntimeErrorFmt(vm, "Expected integer in range "
|
||||||
|
"0x00 to 0xff, got %i.", n);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
double count;
|
||||||
|
if (!pkValidateSlotNumber(vm, 1, &count)) return;
|
||||||
|
|
||||||
|
pkByteBuffer* self = pkGetSelf(vm);
|
||||||
|
pkByteBufferFill(self, vm, (uint8_t) n, (int) count);
|
||||||
|
}
|
||||||
|
|
||||||
void _bytebuffClear(PKVM* vm) {
|
void _bytebuffClear(PKVM* vm) {
|
||||||
// TODO: Should I also zero or reduce the capacity?
|
// TODO: Should I also zero or reduce the capacity?
|
||||||
pkByteBuffer* self = pkGetSelf(vm);
|
pkByteBuffer* self = pkGetSelf(vm);
|
||||||
@ -55,12 +72,8 @@ void _bytebuffWrite(PKVM* vm) {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
case PK_NUMBER: {
|
case PK_NUMBER: {
|
||||||
double n = pkGetSlotNumber(vm, 1);
|
uint32_t i;
|
||||||
if (floor(n) != n) {
|
if (!pkValidateSlotInteger(vm, 1, &i)) return;
|
||||||
pkSetRuntimeErrorFmt(vm, "Expected an integer, got float %g.", n);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
int64_t i = (int64_t) n;
|
|
||||||
if (i < 0x00 || i > 0xff) {
|
if (i < 0x00 || i > 0xff) {
|
||||||
pkSetRuntimeErrorFmt(vm, "Expected integer in range "
|
pkSetRuntimeErrorFmt(vm, "Expected integer in range "
|
||||||
"0x00 to 0xff, got %i.", i);
|
"0x00 to 0xff, got %i.", i);
|
||||||
@ -80,6 +93,10 @@ void _bytebuffWrite(PKVM* vm) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO:
|
||||||
|
case PK_LIST: {
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -144,6 +161,11 @@ void _bytebuffString(PKVM* vm) {
|
|||||||
pkSetSlotStringLength(vm, 0, self->data, self->count);
|
pkSetSlotStringLength(vm, 0, self->data, self->count);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _bytebuffCount(PKVM* vm) {
|
||||||
|
pkByteBuffer* self = pkGetSelf(vm);
|
||||||
|
pkSetSlotNumber(vm, 0, self->count);
|
||||||
|
}
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
/* VECTOR */
|
/* VECTOR */
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
@ -247,9 +269,11 @@ void registerModuleTypes(PKVM* vm) {
|
|||||||
pkClassAddMethod(vm, cls_byte_buffer, "[]", _bytebuffSubscriptGet, 1);
|
pkClassAddMethod(vm, cls_byte_buffer, "[]", _bytebuffSubscriptGet, 1);
|
||||||
pkClassAddMethod(vm, cls_byte_buffer, "[]=", _bytebuffSubscriptSet, 2);
|
pkClassAddMethod(vm, cls_byte_buffer, "[]=", _bytebuffSubscriptSet, 2);
|
||||||
pkClassAddMethod(vm, cls_byte_buffer, "reserve", _bytebuffReserve, 1);
|
pkClassAddMethod(vm, cls_byte_buffer, "reserve", _bytebuffReserve, 1);
|
||||||
|
pkClassAddMethod(vm, cls_byte_buffer, "fill", _bytebuffFill, 2);
|
||||||
pkClassAddMethod(vm, cls_byte_buffer, "clear", _bytebuffClear, 0);
|
pkClassAddMethod(vm, cls_byte_buffer, "clear", _bytebuffClear, 0);
|
||||||
pkClassAddMethod(vm, cls_byte_buffer, "write", _bytebuffWrite, 1);
|
pkClassAddMethod(vm, cls_byte_buffer, "write", _bytebuffWrite, 1);
|
||||||
pkClassAddMethod(vm, cls_byte_buffer, "string", _bytebuffString, 0);
|
pkClassAddMethod(vm, cls_byte_buffer, "string", _bytebuffString, 0);
|
||||||
|
pkClassAddMethod(vm, cls_byte_buffer, "count", _bytebuffCount, 0);
|
||||||
pkReleaseHandle(vm, cls_byte_buffer);
|
pkReleaseHandle(vm, cls_byte_buffer);
|
||||||
|
|
||||||
// TODO: add move mthods.
|
// TODO: add move mthods.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
from lang import write
|
import io
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
## BRAINFUCK IMPLEMENTATION IN POCKETLANG ##
|
## BRAINFUCK IMPLEMENTATION IN POCKETLANG ##
|
||||||
@ -31,6 +31,11 @@ def main()
|
|||||||
execute(hello_world)
|
execute(hello_world)
|
||||||
execute(triangle)
|
execute(triangle)
|
||||||
##execute(fibonacci) This will run endlessly (cannot run test).
|
##execute(fibonacci) This will run endlessly (cannot run test).
|
||||||
|
io.flush()
|
||||||
|
end
|
||||||
|
|
||||||
|
def write(msg)
|
||||||
|
io.write(io.stdout, msg)
|
||||||
end
|
end
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
@ -51,52 +56,52 @@ def execute(expr)
|
|||||||
if ptr >= mem.length then list_append(mem, 0) end
|
if ptr >= mem.length then list_append(mem, 0) end
|
||||||
|
|
||||||
## Decrement the data pointer (to point to the next cell to the left).
|
## Decrement the data pointer (to point to the next cell to the left).
|
||||||
else if c == '<'
|
elif c == '<'
|
||||||
ptr -= 1
|
ptr -= 1
|
||||||
if ptr < 0 then assert(false, "ip < 0") end
|
if ptr < 0 then assert(false, "ip < 0") end
|
||||||
|
|
||||||
## Increment (increase by one) the byte at the data pointer.
|
## Increment (increase by one) the byte at the data pointer.
|
||||||
else if c == '+'
|
elif c == '+'
|
||||||
if mem[ptr] == 255 then mem[ptr] = 0
|
if mem[ptr] == 255 then mem[ptr] = 0
|
||||||
else mem[ptr] += 1 end
|
else mem[ptr] += 1 end
|
||||||
|
|
||||||
## Decrement (decrease by one) the byte at the data pointer.
|
## Decrement (decrease by one) the byte at the data pointer.
|
||||||
else if c == '-'
|
elif c == '-'
|
||||||
if mem[ptr] == 0 then mem[ptr] = 255
|
if mem[ptr] == 0 then mem[ptr] = 255
|
||||||
else mem[ptr] -= 1 end
|
else mem[ptr] -= 1 end
|
||||||
|
|
||||||
## output the byte at the data pointer.
|
## output the byte at the data pointer.
|
||||||
else if c == '.'
|
elif c == '.'
|
||||||
write(chr(mem[ptr]))
|
write(chr(mem[ptr]))
|
||||||
|
|
||||||
else if c == ','
|
elif c == ','
|
||||||
assert(false, "Currently input isn't supported in pocketlang.")
|
assert(false, "Currently input isn't supported in pocketlang.")
|
||||||
|
|
||||||
## if the byte at the data pointer is zero, then instead of moving the
|
## 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
|
## instruction pointer forward to the next command, jump it forward to
|
||||||
## the command after the matching ] command.
|
## the command after the matching ] command.
|
||||||
else if c == '[' and mem[ptr] == 0
|
elif c == '[' and mem[ptr] == 0
|
||||||
open = 0
|
open = 0
|
||||||
while true
|
while true
|
||||||
i += 1
|
i += 1
|
||||||
if expr[i] == ']' and open == 0 then break end
|
if expr[i] == ']' and open == 0 then break end
|
||||||
|
|
||||||
if expr[i] == '[' then open += 1
|
if expr[i] == '[' then open += 1
|
||||||
else if expr[i] == ']' then open -= 1
|
elif expr[i] == ']' then open -= 1
|
||||||
end assert(open >= 0)
|
end assert(open >= 0)
|
||||||
end
|
end
|
||||||
|
|
||||||
## if the byte at the data pointer is nonzero, then instead of moving the
|
## 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
|
## instruction pointer forward to the next command, jump it back to the
|
||||||
## command after the matching [ command
|
## command after the matching [ command
|
||||||
else if c == ']' and mem[ptr] != 0
|
elif c == ']' and mem[ptr] != 0
|
||||||
open = 0
|
open = 0
|
||||||
while true
|
while true
|
||||||
i -= 1
|
i -= 1
|
||||||
if expr[i] == '[' and open == 0 then break end
|
if expr[i] == '[' and open == 0 then break end
|
||||||
|
|
||||||
if expr[i] == ']' then open -= 1
|
if expr[i] == ']' then open -= 1
|
||||||
else if expr[i] == '[' then open += 1
|
elif expr[i] == '[' then open += 1
|
||||||
end assert(open <= 0)
|
end assert(open <= 0)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
for i in 1..100
|
for i in 1..100
|
||||||
if i%3 == 0 and i%5 == 0 then print('fizzbuzz')
|
if i%3 == 0 and i%5 == 0 then print('fizzbuzz')
|
||||||
else if i%3 == 0 then print('fizz')
|
elif i%3 == 0 then print('fizz')
|
||||||
else if i%5 == 0 then print('buzz')
|
elif i%5 == 0 then print('buzz')
|
||||||
else print(i)
|
else print(i)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -10,6 +10,10 @@ assert("testing" == "test" + "ing")
|
|||||||
assert("foo \
|
assert("foo \
|
||||||
bar" == "foo bar")
|
bar" == "foo bar")
|
||||||
|
|
||||||
|
assert('abc ' * 3 == 'abc abc abc ')
|
||||||
|
assert('' * 1000 == '')
|
||||||
|
assert('foo' * 0 == '')
|
||||||
|
|
||||||
assert(-0b10110010 == -178 and 0b11001010 == 202)
|
assert(-0b10110010 == -178 and 0b11001010 == 202)
|
||||||
assert(0b1111111111111111 == 65535)
|
assert(0b1111111111111111 == 65535)
|
||||||
assert(
|
assert(
|
||||||
|
@ -119,7 +119,7 @@ class Path
|
|||||||
def /(other)
|
def /(other)
|
||||||
if other is String
|
if other is String
|
||||||
return Path(self.path + "/" + other)
|
return Path(self.path + "/" + other)
|
||||||
else if other is Path
|
elif other is Path
|
||||||
return Path(self.path + "/" + other.path)
|
return Path(self.path + "/" + other.path)
|
||||||
else
|
else
|
||||||
assert(false, "Invalid type")
|
assert(false, "Invalid type")
|
||||||
|
@ -7,21 +7,21 @@ if true then variable = 42 else unreachable() end
|
|||||||
assert(variable == 42, 'If statement failed.')
|
assert(variable == 42, 'If statement failed.')
|
||||||
|
|
||||||
if false then unreachable()
|
if false then unreachable()
|
||||||
else if true then variable = null
|
elif true then variable = null
|
||||||
else unreachable() end
|
else unreachable() end
|
||||||
assert(variable == null)
|
assert(variable == null)
|
||||||
|
|
||||||
if false then unreachable()
|
if false then unreachable()
|
||||||
else if false then unreachable()
|
elif false then unreachable()
|
||||||
else if false then unreachable()
|
elif false then unreachable()
|
||||||
else variable = "changed" end
|
else variable = "changed" end
|
||||||
assert(variable == "changed")
|
assert(variable == "changed")
|
||||||
|
|
||||||
if false then unreachable()
|
if false then unreachable()
|
||||||
else if true
|
elif true
|
||||||
if false
|
if false
|
||||||
unreachable()
|
unreachable()
|
||||||
else if true
|
elif true
|
||||||
variable = 123
|
variable = 123
|
||||||
else
|
else
|
||||||
unreachable()
|
unreachable()
|
||||||
@ -39,9 +39,9 @@ assert(variable == 1212)
|
|||||||
|
|
||||||
while true
|
while true
|
||||||
variable = 22
|
variable = 22
|
||||||
if true then else if false then end
|
if true then elif false then end
|
||||||
if false
|
if false
|
||||||
else if true
|
elif true
|
||||||
variable += 2300
|
variable += 2300
|
||||||
end
|
end
|
||||||
variable += 1
|
variable += 1
|
||||||
@ -57,7 +57,7 @@ assert(sum == 54)
|
|||||||
|
|
||||||
val = 2
|
val = 2
|
||||||
if val == 1 then val = null
|
if val == 1 then val = null
|
||||||
else if val == 2 then val = 'foo'
|
elif val == 2 then val = 'foo'
|
||||||
else val = null
|
else val = null
|
||||||
end
|
end
|
||||||
assert(val == 'foo')
|
assert(val == 'foo')
|
||||||
@ -66,7 +66,7 @@ val = 2
|
|||||||
if val == 1 then val = null
|
if val == 1 then val = null
|
||||||
else
|
else
|
||||||
if val == 2 then val = 'bar'
|
if val == 2 then val = 'bar'
|
||||||
end ##< Need an extra end since 'else if' != 'else \n if'.
|
end ##< Need an extra end since 'elif' != 'else \n if'.
|
||||||
end
|
end
|
||||||
assert(val == 'bar')
|
assert(val == 'bar')
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
import lang
|
import lang
|
||||||
import lang, path
|
import lang, path
|
||||||
import lang as o, path as p
|
import lang as o, path as p
|
||||||
from lang import write
|
from io import write
|
||||||
from time import sleep as s
|
from time import sleep as s
|
||||||
|
|
||||||
import basics
|
import basics
|
||||||
|
@ -44,27 +44,27 @@ def eval(expr, ind)
|
|||||||
end
|
end
|
||||||
return [val, ind]
|
return [val, ind]
|
||||||
|
|
||||||
else if c == '+'
|
elif c == '+'
|
||||||
r = binary_op(expr, ind)
|
r = binary_op(expr, ind)
|
||||||
if not r[0] then return [null, -1] end
|
if not r[0] then return [null, -1] end
|
||||||
return [r[1] + r[2], r[3]]
|
return [r[1] + r[2], r[3]]
|
||||||
|
|
||||||
else if c == '-'
|
elif c == '-'
|
||||||
r = binary_op(expr, ind)
|
r = binary_op(expr, ind)
|
||||||
if not r[0] then return [null, -1] end
|
if not r[0] then return [null, -1] end
|
||||||
return [r[1] - r[2], r[3]]
|
return [r[1] - r[2], r[3]]
|
||||||
|
|
||||||
else if c == '*'
|
elif c == '*'
|
||||||
r = binary_op(expr, ind)
|
r = binary_op(expr, ind)
|
||||||
if not r[0] then return [null, -1] end
|
if not r[0] then return [null, -1] end
|
||||||
return [r[1] * r[2], r[3]]
|
return [r[1] * r[2], r[3]]
|
||||||
|
|
||||||
else if c == '/'
|
elif c == '/'
|
||||||
r = binary_op(expr, ind)
|
r = binary_op(expr, ind)
|
||||||
if not r[0] then return [null, -1] end
|
if not r[0] then return [null, -1] end
|
||||||
return [r[1] / r[2], r[3]]
|
return [r[1] / r[2], r[3]]
|
||||||
|
|
||||||
else if isnum(c)
|
elif isnum(c)
|
||||||
val = ord(c) - ord('0')
|
val = ord(c) - ord('0')
|
||||||
assert(0 <= val and val < 10)
|
assert(0 <= val and val < 10)
|
||||||
return [val, ind]
|
return [val, ind]
|
||||||
|
Loading…
Reference in New Issue
Block a user