mirror of
https://github.com/zekexiao/pocketlang.git
synced 2025-03-04 05:05:57 +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_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,40 +3039,36 @@ static void compileIfStatement(Compiler* compiler, bool else_if) {
|
||||
|
||||
compileBlockBody(compiler, BLOCK_IF);
|
||||
|
||||
if (match(compiler, TK_ELSE)) {
|
||||
if (match(compiler, TK_ELIF)) {
|
||||
// Jump pass else.
|
||||
emitOpcode(compiler, OP_JUMP);
|
||||
int exit_jump = emitShort(compiler, 0xffff); //< Will be patched.
|
||||
|
||||
if (match(compiler, TK_IF)) { //< Compile 'else if'.
|
||||
// Jump pass else.
|
||||
emitOpcode(compiler, OP_JUMP);
|
||||
int exit_jump = emitShort(compiler, 0xffff); //< Will be patched.
|
||||
// if (false) jump here.
|
||||
patchJump(compiler, ifpatch);
|
||||
|
||||
// if (false) jump here.
|
||||
patchJump(compiler, ifpatch);
|
||||
compilerEnterBlock(compiler);
|
||||
compileIfStatement(compiler, true);
|
||||
compilerExitBlock(compiler);
|
||||
|
||||
compilerEnterBlock(compiler);
|
||||
compileIfStatement(compiler, true);
|
||||
compilerExitBlock(compiler);
|
||||
patchJump(compiler, exit_jump);
|
||||
|
||||
patchJump(compiler, exit_jump);
|
||||
} else if (match(compiler, TK_ELSE)) {
|
||||
// Jump pass else.
|
||||
emitOpcode(compiler, OP_JUMP);
|
||||
int exit_jump = emitShort(compiler, 0xffff); //< Will be patched.
|
||||
|
||||
} else { //< Compile 'else'.
|
||||
|
||||
// Jump pass else.
|
||||
emitOpcode(compiler, OP_JUMP);
|
||||
int exit_jump = emitShort(compiler, 0xffff); //< Will be patched.
|
||||
|
||||
patchJump(compiler, ifpatch);
|
||||
compileBlockBody(compiler, BLOCK_ELSE);
|
||||
patchJump(compiler, exit_jump);
|
||||
}
|
||||
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.");
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
@ -928,7 +946,7 @@ static char* stdinRead(PKVM* vm) {
|
||||
pkByteBufferInit(&buff);
|
||||
char c;
|
||||
do {
|
||||
c = (char)fgetc(stdin);
|
||||
c = (char) fgetc(stdin);
|
||||
if (c == '\n') break;
|
||||
pkByteBufferWrite(&buff, vm, (uint8_t)c);
|
||||
} while (c != EOF);
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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,14 +163,10 @@ 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) {
|
||||
File* self = (File*)pkGetSelf(vm);
|
||||
File* self = (File*) pkGetSelf(vm);
|
||||
self->fp = fp;
|
||||
self->mode = mode;
|
||||
self->closed = false;
|
||||
@ -107,36 +177,145 @@ 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;
|
||||
|
||||
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) {
|
||||
pkSetRuntimeError(vm, "Cannot read from a closed file.");
|
||||
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);
|
||||
File* file = (File*) pkGetSelf(vm);
|
||||
const char* text; uint32_t length;
|
||||
if (!pkValidateSlotString(vm, 1, &text, &length)) return;
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
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, "") {
|
||||
// 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) {
|
||||
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, "write", _fileWrite, 1);
|
||||
pkClassAddMethod(vm, cls_file, "close", _fileClose, 0);
|
||||
pkClassAddMethod(vm, cls_file, "open", _fileOpen, -1);
|
||||
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);
|
||||
|
@ -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 */
|
||||
/*****************************************************************************/
|
||||
@ -244,12 +266,14 @@ void registerModuleTypes(PKVM* vm) {
|
||||
PkHandle* cls_byte_buffer = pkNewClass(vm, "ByteBuffer", NULL, types,
|
||||
_bytebuffNew, _bytebuffDelete);
|
||||
|
||||
pkClassAddMethod(vm, cls_byte_buffer, "[]", _bytebuffSubscriptGet, 1);
|
||||
pkClassAddMethod(vm, cls_byte_buffer, "[]=", _bytebuffSubscriptSet, 2);
|
||||
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, "clear", _bytebuffClear, 0);
|
||||
pkClassAddMethod(vm, cls_byte_buffer, "write", _bytebuffWrite, 1);
|
||||
pkClassAddMethod(vm, cls_byte_buffer, "string", _bytebuffString, 0);
|
||||
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.
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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(
|
||||
|
@ -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")
|
||||
|
@ -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')
|
||||
|
||||
|
@ -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
|
||||
|
@ -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]
|
||||
|
Loading…
Reference in New Issue
Block a user