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:
Thakee Nathees 2022-05-24 01:29:41 +05:30
parent e16debcff0
commit 6e91b66e69
13 changed files with 453 additions and 140 deletions

View File

@ -136,6 +136,7 @@ typedef enum {
TK_WHILE, // while
TK_FOR, // for
TK_IF, // if
TK_ELIF, // elif
TK_ELSE, // else
TK_BREAK, // break
TK_CONTINUE, // continue
@ -200,6 +201,7 @@ static _Keyword _keywords[] = {
{ "while", 5, TK_WHILE },
{ "for", 3, TK_FOR },
{ "if", 2, TK_IF },
{ "elif", 4, TK_ELIF },
{ "else", 4, TK_ELSE },
{ "break", 5, TK_BREAK },
{ "continue", 8, TK_CONTINUE },
@ -1267,7 +1269,8 @@ static void skipNewLines(Compiler* 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) {
if (match(compiler, TK_SEMICOLLON)) {
skipNewLines(compiler);
@ -1278,7 +1281,9 @@ static bool matchEndStatement(Compiler* compiler) {
// 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 (peek(compiler) == TK_END || peek(compiler) == TK_ELSE)
if (peek(compiler) == TK_END
|| peek(compiler) == TK_ELSE
|| peek(compiler) == TK_ELIF)
return true;
return false;
@ -1639,6 +1644,7 @@ GrammarRule rules[] = { // Prefix Infix Infix Precedence
/* TK_WHILE */ NO_RULE,
/* TK_FOR */ NO_RULE,
/* TK_IF */ NO_RULE,
/* TK_ELIF */ NO_RULE,
/* TK_ELSE */ NO_RULE,
/* TK_BREAK */ NO_RULE,
/* TK_CONTINUE */ NO_RULE,
@ -2864,7 +2870,7 @@ static void compileBlockBody(Compiler* compiler, BlockType type) {
_TokenType next = peek(compiler);
while (!(next == TK_END || next == TK_EOF ||
((type == BLOCK_IF) && (next == TK_ELSE)))) {
((type == BLOCK_IF) && (next == TK_ELSE || next == TK_ELIF)))) {
compileStatement(compiler);
skipNewLines(compiler);
@ -3024,7 +3030,7 @@ static void compileExpression(Compiler* compiler) {
parsePrecedence(compiler, PREC_LOWEST);
}
static void compileIfStatement(Compiler* compiler, bool else_if) {
static void compileIfStatement(Compiler* compiler, bool elif) {
skipNewLines(compiler);
compileExpression(compiler); //< Condition.
@ -3033,9 +3039,7 @@ static void compileIfStatement(Compiler* compiler, bool else_if) {
compileBlockBody(compiler, BLOCK_IF);
if (match(compiler, TK_ELSE)) {
if (match(compiler, TK_IF)) { //< Compile 'else if'.
if (match(compiler, TK_ELIF)) {
// Jump pass else.
emitOpcode(compiler, OP_JUMP);
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);
} else { //< Compile 'else'.
} else if (match(compiler, TK_ELSE)) {
// Jump pass else.
emitOpcode(compiler, OP_JUMP);
int exit_jump = emitShort(compiler, 0xffff); //< Will be patched.
@ -3058,15 +3061,14 @@ static void compileIfStatement(Compiler* compiler, bool else_if) {
patchJump(compiler, ifpatch);
compileBlockBody(compiler, BLOCK_ELSE);
patchJump(compiler, exit_jump);
}
} else {
patchJump(compiler, ifpatch);
}
// 'else if' will not consume the 'end' keyword as it'll be leaved to be
// consumed by it's 'if'.
if (!else_if) {
// 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.");
}

View File

@ -237,13 +237,6 @@ static inline bool _callBinaryOpMethod(PKVM* vm, Var self, Var other,
/* 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,
"help([fn:Closure]) -> null\n"
"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, \
(int)strlen(name), argc, fn, DOCSTRING(fn));
// General functions.
INITIALIZE_BUILTIN_FN("type_name", coreTypeName, 1);
INITIALIZE_BUILTIN_FN("help", coreHelp, -1);
INITIALIZE_BUILTIN_FN("assert", coreAssert, -1);
INITIALIZE_BUILTIN_FN("bin", coreBin, 1);
@ -642,31 +634,6 @@ DEF(stdLangDebugBreak,
}
#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) {
#define MODULE_ADD_FN(module, name, fn, argc) \
moduleAddFunctionInternal(vm, module, name, fn, argc, DOCSTRING(fn))
@ -680,7 +647,6 @@ static void initializeCoreModules(PKVM* vm) {
NEW_MODULE(lang, "lang");
MODULE_ADD_FN(lang, "gc", stdLangGC, 0);
MODULE_ADD_FN(lang, "disas", stdLangDisas, 1);
MODULE_ADD_FN(lang, "write", stdLangWrite, -1);
#ifdef DEBUG
MODULE_ADD_FN(lang, "debug_break", stdLangDebugBreak, 0);
#endif
@ -764,6 +730,18 @@ static void _ctorFiber(PKVM* vm) {
#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,
"Number.times(f:fn)\n"
"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)
// 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, "isint", _numberIsint, 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) {
CHECK_NUMERIC_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("*");
return VAR_NULL;
}

View File

@ -6,6 +6,8 @@
// This file contains all the pocketlang public function implementations.
#include <math.h>
#ifndef PK_AMALGAMATED
#include <pocketlang.h>
#include "core.h"
@ -78,7 +80,7 @@ static void stdoutWrite(PKVM* vm, const char* text);
static char* stdinRead(PKVM* vm);
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.");
#if TRACE_MEMORY
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
// use pkGetSlotBool().
PK_PUBLIC bool pkValidateSlotBool(PKVM* vm, int slot, bool* value) {
bool pkValidateSlotBool(PKVM* vm, int slot, bool* value) {
CHECK_FIBER_EXISTS(vm);
VALIDATE_SLOT_INDEX(slot);
@ -567,7 +569,7 @@ PK_PUBLIC bool pkValidateSlotBool(PKVM* vm, int slot, bool* value) {
return true;
}
PK_PUBLIC bool pkValidateSlotNumber(PKVM* vm, int slot, double* value) {
bool pkValidateSlotNumber(PKVM* vm, int slot, double* value) {
CHECK_FIBER_EXISTS(vm);
VALIDATE_SLOT_INDEX(slot);
@ -581,7 +583,23 @@ PK_PUBLIC bool pkValidateSlotNumber(PKVM* vm, int slot, double* value) {
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) {
CHECK_FIBER_EXISTS(vm);
VALIDATE_SLOT_INDEX(slot);
@ -608,7 +626,7 @@ bool pkValidateSlotType(PKVM* vm, int slot, PkVarType type) {
return true;
}
PK_PUBLIC bool pkValidateSlotInstanceOf(PKVM* vm, int slot, int cls) {
bool pkValidateSlotInstanceOf(PKVM* vm, int slot, int cls) {
CHECK_FIBER_EXISTS(vm);
VALIDATE_SLOT_INDEX(slot);
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)));
}
PK_PUBLIC void pkSetSlotStringLength(PKVM* vm, int index,
void pkSetSlotStringLength(PKVM* vm, int index,
const char* value, uint32_t length) {
CHECK_FIBER_EXISTS(vm);
VALIDATE_SLOT_INDEX(index);
@ -751,7 +769,7 @@ bool pkSetAttribute(PKVM* vm, int instance, const char* name, int value) {
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) {
CHECK_FIBER_EXISTS(vm);
CHECK_ARG_NULL(name);
@ -830,7 +848,7 @@ bool pkCallFunction(PKVM* vm, int fn, int argc, int argv, int ret) {
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) {
CHECK_FIBER_EXISTS(vm);
CHECK_ARG_NULL(method);
@ -936,6 +954,7 @@ static char* stdinRead(PKVM* vm) {
char* str = pkRealloc(vm, NULL, buff.count);
memcpy(str, buff.data, buff.count);
pkByteBufferClear(&buff, vm);
return str;
}

View File

@ -98,6 +98,11 @@ typedef void (*pkWriteFn) (PKVM* vm, const char* text);
// string.
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
// code and source for import statements. Return NULL to indicate failure to
// 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.
pkReallocFn realloc_fn;
// I/O callbacks.
pkWriteFn stderr_write;
pkWriteFn stdout_write;
pkReadFn stdin_read;
// Import system callbacks.
pkResolvePathFn resolve_path_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.
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
// if not set a runtime error.
PK_PUBLIC bool pkValidateSlotString(PKVM* vm, int slot,

View File

@ -4,10 +4,56 @@
* Distributed Under The MIT License
*/
#include <math.h>
#ifndef PK_AMALGAMATED
#include "libs.h"
#include "../core/value.h"
#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 */
/*****************************************************************************/
@ -22,13 +68,26 @@
// 'a+' | write to end | create new |
typedef enum {
FMODE_NONE = 0,
FMODE_READ = (1 << 0),
FMODE_WRITE = (1 << 1),
FMODE_APPEND = (1 << 2),
_FMODE_EXT = (1 << 3),
_FMODE_BIN = (1 << 4),
FMODE_READ_EXT = (_FMODE_EXT | FMODE_READ),
FMODE_WRITE_EXT = (_FMODE_EXT | FMODE_WRITE),
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;
typedef struct {
@ -49,9 +108,15 @@ void* _newFile(PKVM* vm) {
void _deleteFile(PKVM* vm, void* ptr) {
File* file = (File*)ptr;
if (!file->closed) {
ASSERT(file->fp != NULL, OOPS);
if (fclose(file->fp) != 0) { /* TODO: error! */ }
file->closed = true;
file->fp = NULL;
} else {
ASSERT(file->fp == NULL, OOPS);
}
pkRealloc(vm, file, 0);
}
@ -73,15 +138,24 @@ DEF(_fileOpen, "") {
if (argc == 2) {
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 {
if (strcmp(mode_str, "r") == 0) { mode = FMODE_READ; 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, "r+") == 0) { mode = FMODE_READ_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, "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.
// If we reached here, that means it's an invalid mode string.
pkSetRuntimeError(vm, "Invalid mode string.");
@ -89,10 +163,6 @@ DEF(_fileOpen, "") {
} 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);
if (fp != NULL) {
@ -107,9 +177,28 @@ DEF(_fileOpen, "") {
}
DEF(_fileRead, "") {
// This TODO is just a blockade from running the bellow code, complete the
// native interface and test before removing it.
TODO;
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);
@ -118,23 +207,113 @@ DEF(_fileRead, "") {
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.");
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];
size_t read = fread((void*)buff, sizeof(char), sizeof(buff), file->fp);
(void) read;
pkSetSlotString(vm, 0, (const char*)buff);
// Allocate string + 1 for the NULL terminator.
char* buff = pkRealloc(vm, NULL, (size_t) count + 1);
ASSERT(buff != NULL, "pkRealloc failed.");
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, "") {
// 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);
const char* text; uint32_t length;
@ -145,22 +324,28 @@ DEF(_fileWrite, "") {
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.");
return;
}
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, "") {
// 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);
if (file->closed) {
ASSERT(file->fp == NULL, OOPS);
pkSetRuntimeError(vm, "File already closed.");
return;
}
@ -168,17 +353,79 @@ DEF(_fileClose, "") {
if (fclose(file->fp) != 0) {
pkSetRuntimeError(vm, "fclose() failed!.");
}
file->fp = NULL;
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) {
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);
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, "getline", _fileGetLine, 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);
pkRegisterModule(vm, io);

View File

@ -36,6 +36,23 @@ void _bytebuffReserve(PKVM* vm) {
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) {
// TODO: Should I also zero or reduce the capacity?
pkByteBuffer* self = pkGetSelf(vm);
@ -55,12 +72,8 @@ void _bytebuffWrite(PKVM* vm) {
return;
case PK_NUMBER: {
double n = pkGetSlotNumber(vm, 1);
if (floor(n) != n) {
pkSetRuntimeErrorFmt(vm, "Expected an integer, got float %g.", n);
return;
}
int64_t i = (int64_t) n;
uint32_t i;
if (!pkValidateSlotInteger(vm, 1, &i)) return;
if (i < 0x00 || i > 0xff) {
pkSetRuntimeErrorFmt(vm, "Expected integer in range "
"0x00 to 0xff, got %i.", i);
@ -80,6 +93,10 @@ void _bytebuffWrite(PKVM* vm) {
return;
}
// TODO:
case PK_LIST: {
}
default:
break;
}
@ -144,6 +161,11 @@ void _bytebuffString(PKVM* vm) {
pkSetSlotStringLength(vm, 0, self->data, self->count);
}
void _bytebuffCount(PKVM* vm) {
pkByteBuffer* self = pkGetSelf(vm);
pkSetSlotNumber(vm, 0, self->count);
}
/*****************************************************************************/
/* VECTOR */
/*****************************************************************************/
@ -247,9 +269,11 @@ void registerModuleTypes(PKVM* vm) {
pkClassAddMethod(vm, cls_byte_buffer, "[]", _bytebuffSubscriptGet, 1);
pkClassAddMethod(vm, cls_byte_buffer, "[]=", _bytebuffSubscriptSet, 2);
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, "write", _bytebuffWrite, 1);
pkClassAddMethod(vm, cls_byte_buffer, "string", _bytebuffString, 0);
pkClassAddMethod(vm, cls_byte_buffer, "count", _bytebuffCount, 0);
pkReleaseHandle(vm, cls_byte_buffer);
// TODO: add move mthods.

View File

@ -1,4 +1,4 @@
from lang import write
import io
###############################################################################
## BRAINFUCK IMPLEMENTATION IN POCKETLANG ##
@ -31,6 +31,11 @@ def main()
execute(hello_world)
execute(triangle)
##execute(fibonacci) This will run endlessly (cannot run test).
io.flush()
end
def write(msg)
io.write(io.stdout, msg)
end
###############################################################################
@ -51,52 +56,52 @@ def execute(expr)
if ptr >= mem.length then list_append(mem, 0) end
## Decrement the data pointer (to point to the next cell to the left).
else if c == '<'
elif c == '<'
ptr -= 1
if ptr < 0 then assert(false, "ip < 0") end
## Increment (increase by one) the byte at the data pointer.
else if c == '+'
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.
else if c == '-'
elif c == '-'
if mem[ptr] == 0 then mem[ptr] = 255
else mem[ptr] -= 1 end
## output the byte at the data pointer.
else if c == '.'
elif c == '.'
write(chr(mem[ptr]))
else if c == ','
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.
else if c == '[' and mem[ptr] == 0
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
else 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
else if c == ']' and mem[ptr] != 0
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
else if expr[i] == '[' then open += 1
elif expr[i] == '[' then open += 1
end assert(open <= 0)
end

View File

@ -2,8 +2,8 @@
for i in 1..100
if i%3 == 0 and i%5 == 0 then print('fizzbuzz')
else if i%3 == 0 then print('fizz')
else if i%5 == 0 then print('buzz')
elif i%3 == 0 then print('fizz')
elif i%5 == 0 then print('buzz')
else print(i)
end
end

View File

@ -10,6 +10,10 @@ assert("testing" == "test" + "ing")
assert("foo \
bar" == "foo bar")
assert('abc ' * 3 == 'abc abc abc ')
assert('' * 1000 == '')
assert('foo' * 0 == '')
assert(-0b10110010 == -178 and 0b11001010 == 202)
assert(0b1111111111111111 == 65535)
assert(

View File

@ -119,7 +119,7 @@ class Path
def /(other)
if other is String
return Path(self.path + "/" + other)
else if other is Path
elif other is Path
return Path(self.path + "/" + other.path)
else
assert(false, "Invalid type")

View File

@ -7,21 +7,21 @@ if true then variable = 42 else unreachable() end
assert(variable == 42, 'If statement failed.')
if false then unreachable()
else if true then variable = null
elif true then variable = null
else unreachable() end
assert(variable == null)
if false then unreachable()
else if false then unreachable()
else if false then unreachable()
elif false then unreachable()
elif false then unreachable()
else variable = "changed" end
assert(variable == "changed")
if false then unreachable()
else if true
elif true
if false
unreachable()
else if true
elif true
variable = 123
else
unreachable()
@ -39,9 +39,9 @@ assert(variable == 1212)
while true
variable = 22
if true then else if false then end
if true then elif false then end
if false
else if true
elif true
variable += 2300
end
variable += 1
@ -57,7 +57,7 @@ assert(sum == 54)
val = 2
if val == 1 then val = null
else if val == 2 then val = 'foo'
elif val == 2 then val = 'foo'
else val = null
end
assert(val == 'foo')
@ -66,7 +66,7 @@ val = 2
if val == 1 then val = null
else
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
assert(val == 'bar')

View File

@ -3,7 +3,7 @@
import lang
import lang, path
import lang as o, path as p
from lang import write
from io import write
from time import sleep as s
import basics

View File

@ -44,27 +44,27 @@ def eval(expr, ind)
end
return [val, ind]
else if c == '+'
elif c == '+'
r = binary_op(expr, ind)
if not r[0] then return [null, -1] end
return [r[1] + r[2], r[3]]
else if c == '-'
elif c == '-'
r = binary_op(expr, ind)
if not r[0] then return [null, -1] end
return [r[1] - r[2], r[3]]
else if c == '*'
elif c == '*'
r = binary_op(expr, ind)
if not r[0] then return [null, -1] end
return [r[1] * r[2], r[3]]
else if c == '/'
elif c == '/'
r = binary_op(expr, ind)
if not r[0] then return [null, -1] end
return [r[1] / r[2], r[3]]
else if isnum(c)
elif isnum(c)
val = ord(c) - ord('0')
assert(0 <= val and val < 10)
return [val, ind]