math module moved to cli/modules

This commit is contained in:
Thakee Nathees 2022-04-21 21:37:38 +05:30
parent 179026294d
commit 2fe579d9cc
18 changed files with 522 additions and 547 deletions

View File

@ -15,6 +15,7 @@
#include "modules/std_io.c"
#include "modules/std_path.c"
#include "modules/std_math.c"
/*****************************************************************************/
/* THIRDPARTY SOURCES */

View File

@ -16,12 +16,14 @@
void registerModuleIO(PKVM* vm);
void registerModulePath(PKVM* vm);
void registerModuleMath(PKVM* vm);
// Registers all the cli modules.
#define REGISTER_ALL_MODULES(vm) \
do { \
registerModuleIO(vm); \
registerModulePath(vm); \
registerModuleMath(vm); \
} while (false)
/*****************************************************************************/
@ -35,6 +37,16 @@ void registerModulePath(PKVM* vm);
// callback.
#define FREE_OBJ(ptr) free(ptr)
// Returns the docstring of the function, which is a static const char* defined
// just above the function by the DEF() macro below.
#define DOCSTRING(fn) __doc_##fn
// A macro to declare a function, with docstring, which is defined as
// ___doc_<fn> = docstring; That'll used to generate function help text.
#define DEF(fn, docstring) \
static const char* DOCSTRING(fn) = docstring; \
static void fn(PKVM* vm)
/*****************************************************************************/
/* SHARED FUNCTIONS */
/*****************************************************************************/

View File

@ -56,7 +56,7 @@ void _deleteFile(void* ptr) {
/* FILE MODULE FUNCTIONS */
/*****************************************************************************/
static void _fileOpen(PKVM* vm) {
DEF(_fileOpen, "") {
int argc = pkGetArgc(vm);
if (!pkCheckArgcRange(vm, argc, 1, 2)) return;
@ -103,7 +103,7 @@ static void _fileOpen(PKVM* vm) {
}
}
static void _fileRead(PKVM* vm) {
DEF(_fileRead, "") {
// This TODO is just a blockade from running the bellow code, complete the
// native interface and test before removing it.
TODO;
@ -126,7 +126,7 @@ static void _fileRead(PKVM* vm) {
pkReturnString(vm, (const char*)buff);
}
static void _fileWrite(PKVM* vm) {
DEF(_fileWrite, "") {
// This TODO is just a blockade from running the bellow code, complete the
// native interface and test before removing it.
TODO;
@ -148,7 +148,7 @@ static void _fileWrite(PKVM* vm) {
fwrite(text, sizeof(char), (size_t)length, file->fp);
}
static void _fileClose(PKVM* vm) {
DEF(_fileClose, "") {
// This TODO is just a blockade from running the bellow code, complete the
// native interface and test before removing it.
TODO;

218
cli/modules/std_math.c Normal file
View File

@ -0,0 +1,218 @@
/*
* Copyright (c) 2020-2022 Thakee Nathees
* Copyright (c) 2021-2022 Pocketlang Contributors
* Distributed Under The MIT License
*/
#include "modules.h"
#include <math.h>
// M_PI is non standard. The macro _USE_MATH_DEFINES defining before importing
// <math.h> will define the constants for MSVC. But for a portable solution,
// we're defining it ourselves if it isn't already.
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
DEF(stdMathFloor,
"floor(value:num) -> num\n") {
double num;
if (!pkGetArgNumber(vm, 1, &num)) return;
pkReturnNumber(vm, floor(num));
}
DEF(stdMathCeil,
"ceil(value:num) -> num\n") {
double num;
if (!pkGetArgNumber(vm, 1, &num)) return;
pkReturnNumber(vm, ceil(num));
}
DEF(stdMathPow,
"pow(value:num) -> num\n") {
double num, ex;
if (!pkGetArgNumber(vm, 1, &num)) return;
if (!pkGetArgNumber(vm, 2, &ex)) return;
pkReturnNumber(vm, pow(num, ex));
}
DEF(stdMathSqrt,
"sqrt(value:num) -> num\n") {
double num;
if (!pkGetArgNumber(vm, 1, &num)) return;
pkReturnNumber(vm, sqrt(num));
}
DEF(stdMathAbs,
"abs(value:num) -> num\n") {
double num;
if (!pkGetArgNumber(vm, 1, &num)) return;
if (num < 0) num = -num;
pkReturnNumber(vm, num);
}
DEF(stdMathSign,
"sign(value:num) -> num\n") {
double num;
if (!pkGetArgNumber(vm, 1, &num)) return;
if (num < 0) num = -1;
else if (num > 0) num = +1;
else num = 0;
pkReturnNumber(vm, num);
}
DEF(stdMathSine,
"sin(rad:num) -> num\n"
"Return the sine value of the argument [rad] which is an angle expressed "
"in radians.") {
double rad;
if (!pkGetArgNumber(vm, 1, &rad)) return;
pkReturnNumber(vm, sin(rad));
}
DEF(stdMathCosine,
"cos(rad:num) -> num\n"
"Return the cosine value of the argument [rad] which is an angle expressed "
"in radians.") {
double rad;
if (!pkGetArgNumber(vm, 1, &rad)) return;
pkReturnNumber(vm, cos(rad));
}
DEF(stdMathTangent,
"tan(rad:num) -> num\n"
"Return the tangent value of the argument [rad] which is an angle expressed "
"in radians.") {
double rad;
if (!pkGetArgNumber(vm, 1, &rad)) return;
pkReturnNumber(vm, tan(rad));
}
DEF(stdMathSinh,
"sinh(val) -> val\n"
"Return the hyperbolic sine value of the argument [val].") {
double val;
if (!pkGetArgNumber(vm, 1, &val)) return;
pkReturnNumber(vm, sinh(val));
}
DEF(stdMathCosh,
"cosh(val) -> val\n"
"Return the hyperbolic cosine value of the argument [val].") {
double val;
if (!pkGetArgNumber(vm, 1, &val)) return;
pkReturnNumber(vm, cosh(val));
}
DEF(stdMathTanh,
"tanh(val) -> val\n"
"Return the hyperbolic tangent value of the argument [val].") {
double val;
if (!pkGetArgNumber(vm, 1, &val)) return;
pkReturnNumber(vm, tanh(val));
}
DEF(stdMathArcSine,
"asin(num) -> num\n"
"Return the arcsine value of the argument [num] which is an angle "
"expressed in radians.") {
double num;
if (!pkGetArgNumber(vm, 1, &num)) return;
if (num < -1 || 1 < num) {
pkSetRuntimeError(vm, "Argument should be between -1 and +1");
}
pkReturnNumber(vm, asin(num));
}
DEF(stdMathArcCosine,
"acos(num) -> num\n"
"Return the arc cosine value of the argument [num] which is "
"an angle expressed in radians.") {
double num;
if (!pkGetArgNumber(vm, 1, &num)) return;
if (num < -1 || 1 < num) {
pkSetRuntimeError(vm, "Argument should be between -1 and +1");
}
pkReturnNumber(vm, acos(num));
}
DEF(stdMathArcTangent,
"atan(num) -> num\n"
"Return the arc tangent value of the argument [num] which is "
"an angle expressed in radians.") {
double num;
if (!pkGetArgNumber(vm, 1, &num)) return;
pkReturnNumber(vm, atan(num));
}
DEF(stdMathLog10,
"log10(value:num) -> num\n"
"Return the logarithm to base 10 of argument [value]") {
double num;
if (!pkGetArgNumber(vm, 1, &num)) return;
pkReturnNumber(vm, log10(num));
}
DEF(stdMathRound,
"round(value:num) -> num\n"
"Round to nearest integer, away from zero and return the number.") {
double num;
if (!pkGetArgNumber(vm, 1, &num)) return;
pkReturnNumber(vm, round(num));
}
void registerModuleMath(PKVM* vm) {
PkHandle* math = pkNewModule(vm, "math");
pkModuleAddFunction(vm, math, "floor", stdMathFloor, 1);
pkModuleAddFunction(vm, math, "ceil", stdMathCeil, 1);
pkModuleAddFunction(vm, math, "pow", stdMathPow, 2);
pkModuleAddFunction(vm, math, "sqrt", stdMathSqrt, 1);
pkModuleAddFunction(vm, math, "abs", stdMathAbs, 1);
pkModuleAddFunction(vm, math, "sign", stdMathSign, 1);
pkModuleAddFunction(vm, math, "sin", stdMathSine, 1);
pkModuleAddFunction(vm, math, "cos", stdMathCosine, 1);
pkModuleAddFunction(vm, math, "tan", stdMathTangent, 1);
pkModuleAddFunction(vm, math, "sinh", stdMathSinh, 1);
pkModuleAddFunction(vm, math, "cosh", stdMathCosh, 1);
pkModuleAddFunction(vm, math, "tanh", stdMathTanh, 1);
pkModuleAddFunction(vm, math, "asin", stdMathArcSine, 1);
pkModuleAddFunction(vm, math, "acos", stdMathArcCosine, 1);
pkModuleAddFunction(vm, math, "atan", stdMathArcTangent, 1);
pkModuleAddFunction(vm, math, "log10", stdMathLog10, 1);
pkModuleAddFunction(vm, math, "round", stdMathRound, 1);
// FIXME:
// Refactor native type interface and add PI as a global to the module.
//
// Note that currently it's mutable (since it's a global variable, not
// constant and pocketlang doesn't support constant) so the user shouldn't
// modify the PI, like in python.
//pkModuleAddGlobal(vm, math, "PI", Handle-Of-PI);
pkRegisterModule(vm, math);
pkReleaseHandle(vm, math);
}

View File

@ -4,6 +4,8 @@
* Distributed Under The MIT License
*/
#include "modules.h"
#include "thirdparty/cwalk/cwalk.h"
#if defined(_WIN32) && (defined(_MSC_VER) || defined(__TINYC__))
#include "thirdparty/dirent/dirent.h"
@ -90,13 +92,13 @@ static inline size_t pathAbs(const char* path, char* buff, size_t buff_size) {
/* PATH MODULE FUNCTIONS */
/*****************************************************************************/
static void _pathSetStyleUnix(PKVM* vm) {
DEF(_pathSetStyleUnix, "") {
bool value;
if (!pkGetArgBool(vm, 1, &value)) return;
cwk_path_set_style((value) ? CWK_STYLE_UNIX : CWK_STYLE_WINDOWS);
}
static void _pathGetCWD(PKVM* vm) {
DEF(_pathGetCWD, "") {
char cwd[FILENAME_MAX];
if (get_cwd(cwd, sizeof(cwd)) == NULL) {
// TODO: Handle error.
@ -104,7 +106,7 @@ static void _pathGetCWD(PKVM* vm) {
pkReturnString(vm, cwd);
}
static void _pathAbspath(PKVM* vm) {
DEF(_pathAbspath, "") {
const char* path;
if (!pkGetArgString(vm, 1, &path, NULL)) return;
@ -113,7 +115,7 @@ static void _pathAbspath(PKVM* vm) {
pkReturnStringLength(vm, abspath, len);
}
static void _pathRelpath(PKVM* vm) {
DEF(_pathRelpath, "") {
const char* from, * path;
if (!pkGetArgString(vm, 1, &from, NULL)) return;
if (!pkGetArgString(vm, 2, &path, NULL)) return;
@ -130,7 +132,7 @@ static void _pathRelpath(PKVM* vm) {
pkReturnStringLength(vm, result, len);
}
static void _pathJoin(PKVM* vm) {
DEF(_pathJoin, "") {
const char* paths[MAX_JOIN_PATHS + 1]; // +1 for NULL.
int argc = pkGetArgc(vm);
@ -150,7 +152,7 @@ static void _pathJoin(PKVM* vm) {
pkReturnStringLength(vm, result, len);
}
static void _pathNormalize(PKVM* vm) {
DEF(_pathNormalize, "") {
const char* path;
if (!pkGetArgString(vm, 1, &path, NULL)) return;
@ -159,7 +161,7 @@ static void _pathNormalize(PKVM* vm) {
pkReturnStringLength(vm, result, len);
}
static void _pathBaseName(PKVM* vm) {
DEF(_pathBaseName, "") {
const char* path;
if (!pkGetArgString(vm, 1, &path, NULL)) return;
@ -169,7 +171,7 @@ static void _pathBaseName(PKVM* vm) {
pkReturnString(vm, base_name);
}
static void _pathDirName(PKVM* vm) {
DEF(_pathDirName, "") {
const char* path;
if (!pkGetArgString(vm, 1, &path, NULL)) return;
@ -178,14 +180,14 @@ static void _pathDirName(PKVM* vm) {
pkReturnStringLength(vm, path, length);
}
static void _pathIsPathAbs(PKVM* vm) {
DEF(_pathIsPathAbs, "") {
const char* path;
if (!pkGetArgString(vm, 1, &path, NULL)) return;
pkReturnBool(vm, cwk_path_is_absolute(path));
}
static void _pathGetExtension(PKVM* vm) {
DEF(_pathGetExtension, "") {
const char* path;
if (!pkGetArgString(vm, 1, &path, NULL)) return;
@ -198,19 +200,19 @@ static void _pathGetExtension(PKVM* vm) {
}
}
static void _pathExists(PKVM* vm) {
DEF(_pathExists, "") {
const char* path;
if (!pkGetArgString(vm, 1, &path, NULL)) return;
pkReturnBool(vm, pathIsExists(path));
}
static void _pathIsFile(PKVM* vm) {
DEF(_pathIsFile, "") {
const char* path;
if (!pkGetArgString(vm, 1, &path, NULL)) return;
pkReturnBool(vm, pathIsFileExists(path));
}
static void _pathIsDir(PKVM* vm) {
DEF(_pathIsDir, "") {
const char* path;
if (!pkGetArgString(vm, 1, &path, NULL)) return;
pkReturnBool(vm, pathIsDirectoryExists(path));

View File

@ -15,13 +15,6 @@
#include "pk_utils.h"
#include "pk_vm.h"
// M_PI is non standard. The macro _USE_MATH_DEFINES defining before importing
// <math.h> will define the constants for MSVC. But for a portable solution,
// we're defining it ourselves if it isn't already.
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
// Returns the docstring of the function, which is a static const char* defined
// just above the function by the DEF() macro below.
#define DOCSTRING(fn) _pk_doc_##fn
@ -109,9 +102,16 @@ void pkClassAddMethod(PKVM* vm, PkHandle* cls,
// won't be garbage collected (class handle has reference to the module).
Closure* method = newClosure(vm, fn);
vmPushTempRef(vm, &method->_super); // method.
pkClosureBufferWrite(&class_->methods, vm, method);
vmPopTempRef(vm); // method.
// FIXME: name "_init" is literal everywhere.
if (strcmp(name, "_init") == 0) {
class_->ctor = method;
} else {
vmPushTempRef(vm, &method->_super); // method.
pkClosureBufferWrite(&class_->methods, vm, method);
vmPopTempRef(vm); // method.
}
}
void* pkGetSelf(const PKVM* vm) {
@ -535,7 +535,7 @@ DEF(coreAssert,
DEF(coreBin,
"bin(value:num) -> string\n"
"Returns as a binary value string with '0x' prefix.") {
"Returns as a binary value string with '0b' prefix.") {
int64_t value;
if (!validateInteger(vm, ARG(1), &value, "Argument 1")) return;
@ -613,6 +613,35 @@ DEF(coreToString,
RET(VAR_OBJ(toString(vm, ARG(1))));
}
DEF(coreChr,
"chr(value:num) -> string\n"
"Returns the ASCII string value of the integer argument.") {
int64_t num;
if (!validateInteger(vm, ARG(1), &num, "Argument 1")) return;
if (!IS_NUM_BYTE(num)) {
RET_ERR(newString(vm, "The number is not in a byte range."));
}
char c = (char)num;
RET(VAR_OBJ(newStringLength(vm, &c, 1)));
}
DEF(coreOrd,
"ord(value:string) -> num\n"
"Returns integer value of the given ASCII character.") {
String* c;
if (!validateArgString(vm, 1, &c)) return;
if (c->length != 1) {
RET_ERR(newString(vm, "Expected a string of length 1."));
} else {
RET(VAR_NUM((double)c->data[0]));
}
}
DEF(corePrint,
"print(...) -> void\n"
"Write each argument as space seperated, to the stdout and ends with a "
@ -700,35 +729,6 @@ DEF(coreStrSub,
RET(VAR_OBJ(newStringLength(vm, str->data + pos, (uint32_t)len)));
}
DEF(coreStrChr,
"str_chr(value:num) -> string\n"
"Returns the ASCII string value of the integer argument.") {
int64_t num;
if (!validateInteger(vm, ARG(1), &num, "Argument 1")) return;
if (!IS_NUM_BYTE(num)) {
RET_ERR(newString(vm, "The number is not in a byte range."));
}
char c = (char)num;
RET(VAR_OBJ(newStringLength(vm, &c, 1)));
}
DEF(coreStrOrd,
"str_ord(value:string) -> num\n"
"Returns integer value of the given ASCII character.") {
String* c;
if (!validateArgString(vm, 1, &c)) return;
if (c->length != 1) {
RET_ERR(newString(vm, "Expected a string of length 1."));
} else {
RET(VAR_NUM((double)c->data[0]));
}
}
// List functions.
// ---------------
@ -806,14 +806,17 @@ static void initializeBuiltinFunctions(PKVM* vm) {
INITIALIZE_BUILTIN_FN("hex", coreHex, 1);
INITIALIZE_BUILTIN_FN("yield", coreYield, -1);
INITIALIZE_BUILTIN_FN("to_string", coreToString, 1);
INITIALIZE_BUILTIN_FN("chr", coreChr, 1);
INITIALIZE_BUILTIN_FN("ord", coreOrd, 1);
INITIALIZE_BUILTIN_FN("print", corePrint, -1);
INITIALIZE_BUILTIN_FN("input", coreInput, -1);
INITIALIZE_BUILTIN_FN("exit", coreExit, -1);
// FIXME:
// move this functions as methods. and make "append()" a builtin.
// String functions.
INITIALIZE_BUILTIN_FN("str_sub", coreStrSub, 3);
INITIALIZE_BUILTIN_FN("str_chr", coreStrChr, 1);
INITIALIZE_BUILTIN_FN("str_ord", coreStrOrd, 1);
// List functions.
INITIALIZE_BUILTIN_FN("list_append", coreListAppend, 2);
@ -934,192 +937,6 @@ DEF(stdLangWrite,
}
}
// TODO: Move math to cli as it's not part of the pocketlang core.
//
// 'math' library methods.
// -----------------------
DEF(stdMathFloor,
"floor(value:num) -> num\n") {
double num;
if (!validateNumeric(vm, ARG(1), &num, "Argument 1")) return;
RET(VAR_NUM(floor(num)));
}
DEF(stdMathCeil,
"ceil(value:num) -> num\n") {
double num;
if (!validateNumeric(vm, ARG(1), &num, "Argument 1")) return;
RET(VAR_NUM(ceil(num)));
}
DEF(stdMathPow,
"pow(value:num) -> num\n") {
double num, ex;
if (!validateNumeric(vm, ARG(1), &num, "Argument 1")) return;
if (!validateNumeric(vm, ARG(2), &ex, "Argument 2")) return;
RET(VAR_NUM(pow(num, ex)));
}
DEF(stdMathSqrt,
"sqrt(value:num) -> num\n") {
double num;
if (!validateNumeric(vm, ARG(1), &num, "Argument 1")) return;
RET(VAR_NUM(sqrt(num)));
}
DEF(stdMathAbs,
"abs(value:num) -> num\n") {
double num;
if (!validateNumeric(vm, ARG(1), &num, "Argument 1")) return;
if (num < 0) num = -num;
RET(VAR_NUM(num));
}
DEF(stdMathSign,
"sign(value:num) -> num\n") {
double num;
if (!validateNumeric(vm, ARG(1), &num, "Argument 1")) return;
if (num < 0) num = -1;
else if (num > 0) num = +1;
else num = 0;
RET(VAR_NUM(num));
}
DEF(stdMathHash,
"hash(value:var) -> num\n"
"Return the hash value of the variable, if it's not hashable it'll "
"return null.") {
if (IS_OBJ(ARG(1))) {
if (!isObjectHashable(AS_OBJ(ARG(1))->type)) {
RET(VAR_NULL);
}
}
RET(VAR_NUM((double)varHashValue(ARG(1))));
}
DEF(stdMathSine,
"sin(rad:num) -> num\n"
"Return the sine value of the argument [rad] which is an angle expressed "
"in radians.") {
double rad;
if (!validateNumeric(vm, ARG(1), &rad, "Argument 1")) return;
RET(VAR_NUM(sin(rad)));
}
DEF(stdMathCosine,
"cos(rad:num) -> num\n"
"Return the cosine value of the argument [rad] which is an angle expressed "
"in radians.") {
double rad;
if (!validateNumeric(vm, ARG(1), &rad, "Argument 1")) return;
RET(VAR_NUM(cos(rad)));
}
DEF(stdMathTangent,
"tan(rad:num) -> num\n"
"Return the tangent value of the argument [rad] which is an angle expressed "
"in radians.") {
double rad;
if (!validateNumeric(vm, ARG(1), &rad, "Argument 1")) return;
RET(VAR_NUM(tan(rad)));
}
DEF(stdMathSinh,
"sinh(val) -> val\n"
"Return the hyperbolic sine value of the argument [val].") {
double val;
if (!validateNumeric(vm, ARG(1), &val, "Argument 1")) return;
RET(VAR_NUM(sinh(val)));
}
DEF(stdMathCosh,
"cosh(val) -> val\n"
"Return the hyperbolic cosine value of the argument [val].") {
double val;
if (!validateNumeric(vm, ARG(1), &val, "Argument 1")) return;
RET(VAR_NUM(cosh(val)));
}
DEF(stdMathTanh,
"tanh(val) -> val\n"
"Return the hyperbolic tangent value of the argument [val].") {
double val;
if (!validateNumeric(vm, ARG(1), &val, "Argument 1")) return;
RET(VAR_NUM(tanh(val)));
}
DEF(stdMathArcSine,
"asin(num) -> num\n"
"Return the arcsine value of the argument [num] which is an angle "
"expressed in radians.") {
double num;
if (!validateNumeric(vm, ARG(1), &num, "Argument 1")) return;
if (num < -1 || 1 < num) {
RET_ERR(newString(vm, "Argument should be between -1 and +1"));
}
RET(VAR_NUM(asin(num)));
}
DEF(stdMathArcCosine,
"acos(num) -> num\n"
"Return the arc cosine value of the argument [num] which is "
"an angle expressed in radians.") {
double num;
if (!validateNumeric(vm, ARG(1), &num, "Argument 1")) return;
if (num < -1 || 1 < num) {
RET_ERR(newString(vm, "Argument should be between -1 and +1"));
}
RET(VAR_NUM(acos(num)));
}
DEF(stdMathArcTangent,
"atan(num) -> num\n"
"Return the arc tangent value of the argument [num] which is "
"an angle expressed in radians.") {
double num;
if (!validateNumeric(vm, ARG(1), &num, "Argument 1")) return;
RET(VAR_NUM(atan(num)));
}
DEF(stdMathLog10,
"log10(value:num) -> num\n"
"Return the logarithm to base 10 of argument [value]") {
double num;
if (!validateNumeric(vm, ARG(1), &num, "Argument 1")) return;
RET(VAR_NUM(log10(num)));
}
DEF(stdMathRound,
"round(value:num) -> num\n"
"Round to nearest integer, away from zero and return the number.") {
double num;
if (!validateNumeric(vm, ARG(1), &num, "Argument 1")) return;
RET(VAR_NUM(round(num)));
}
static void initializeCoreModules(PKVM* vm) {
#define MODULE_ADD_FN(module, name, fn, argc) \
moduleAddFunctionInternal(vm, module, name, fn, argc, DOCSTRING(fn))
@ -1139,31 +956,6 @@ static void initializeCoreModules(PKVM* vm) {
MODULE_ADD_FN(lang, "debug_break", stdLangDebugBreak, 0);
#endif
NEW_MODULE(math, "math");
MODULE_ADD_FN(math, "floor", stdMathFloor, 1);
MODULE_ADD_FN(math, "ceil", stdMathCeil, 1);
MODULE_ADD_FN(math, "pow", stdMathPow, 2);
MODULE_ADD_FN(math, "sqrt", stdMathSqrt, 1);
MODULE_ADD_FN(math, "abs", stdMathAbs, 1);
MODULE_ADD_FN(math, "sign", stdMathSign, 1);
MODULE_ADD_FN(math, "hash", stdMathHash, 1);
MODULE_ADD_FN(math, "sin", stdMathSine, 1);
MODULE_ADD_FN(math, "cos", stdMathCosine, 1);
MODULE_ADD_FN(math, "tan", stdMathTangent, 1);
MODULE_ADD_FN(math, "sinh", stdMathSinh, 1);
MODULE_ADD_FN(math, "cosh", stdMathCosh, 1);
MODULE_ADD_FN(math, "tanh", stdMathTanh, 1);
MODULE_ADD_FN(math, "asin", stdMathArcSine, 1);
MODULE_ADD_FN(math, "acos", stdMathArcCosine, 1);
MODULE_ADD_FN(math, "atan", stdMathArcTangent, 1);
MODULE_ADD_FN(math, "log10", stdMathLog10, 1);
MODULE_ADD_FN(math, "round", stdMathRound, 1);
// Note that currently it's mutable (since it's a global variable, not
// constant and pocketlang doesn't support constant) so the user shouldn't
// modify the PI, like in python.
moduleAddGlobal(vm, math, "PI", 2, VAR_NUM(M_PI));
#undef MODULE_ADD_FN
#undef NEW_MODULE
}

View File

@ -779,7 +779,8 @@ List* listJoin(PKVM* vm, List* l1, List* l2) {
return list;
}
// Return a hash value for the object.
// Return a hash value for the object. Only String and Range objects can be
// hashable.
static uint32_t _hashObject(Object* obj) {
ASSERT(isObjectHashable(obj->type),
@ -790,28 +791,10 @@ static uint32_t _hashObject(Object* obj) {
case OBJ_STRING:
return ((String*)obj)->hash;
case OBJ_LIST:
case OBJ_MAP:
goto L_unhashable;
case OBJ_RANGE:
{
case OBJ_RANGE: {
Range* range = (Range*)obj;
return utilHashNumber(range->from) ^ utilHashNumber(range->to);
}
case OBJ_MODULE:
case OBJ_FUNC:
case OBJ_FIBER:
case OBJ_CLASS:
case OBJ_INST:
TODO;
UNREACHABLE();
default:
L_unhashable:
UNREACHABLE();
break;
}
UNREACHABLE();
@ -1346,8 +1329,8 @@ bool isValuesEqual(Var v1, Var v2) {
}
bool isObjectHashable(ObjectType type) {
// Only list and map are un-hashable.
return type != OBJ_LIST && type != OBJ_MAP;
// Only String and Range are hashable (since they're immutable).
return type == OBJ_STRING || type == OBJ_RANGE;
}
// This will prevent recursive list/map from crash when calling to_string, by

View File

@ -67,7 +67,7 @@ def execute(expr)
## output the byte at the data pointer.
else if c == '.'
write(str_chr(mem[ptr]))
write(chr(mem[ptr]))
else if c == ','
assert(false, "Currently input isn't supported in pocketlang.")

View File

@ -3,7 +3,11 @@
##
## PI/4 = 1 - 1/3 + 1/5 - 1/7 + 1/9 - ...
from math import abs, PI
from math import abs
## Temproarly we cannot register variables on native modules
## will be implemented soon. So defining the PI here.
PI = 3.14159265358979323846
pi_by_4 = 0; sign = -1
for i in 1..100000

View File

@ -37,11 +37,9 @@ assert(!('foo' in {'bar':'baz'}))
## Builtin functions tests.
assert(to_string(42) == '42')
## Core module test.
import math
h1 = math.hash("testing"); h2 = math.hash("test" + "ing")
assert(h1 == h2)
assert(math.ceil(1.1) == math.floor(2.9))
## FIXME: add hash function.
##h1 = math.hash("testing"); h2 = math.hash("test" + "ing")
##assert(h1 == h2)
## Logical statement test
val = 0; a = false; b = true

View File

@ -1,8 +1,5 @@
## Core builtin functions and attribute tests.
## Math functions
from math import *
assert(hex(12648430) == '0xc0ffee')
assert(hex(255) == '0xff' and hex(10597059) == '0xa1b2c3')
assert(hex(-4294967295) == '-0xffffffff') ## the largest.
@ -48,61 +45,6 @@ assert(r.as_list == [1, 2, 3, 4])
assert(r.first == 1)
assert(r.last == 5)
assert(sin(0) == 0)
assert(sin(PI/2) == 1)
threshold = 0.0000000000001
assert(abs(cos(PI/3) - 0.5) < threshold )
assert(abs(tan(PI/4) - 1.0) < threshold )
for i in 0..1000
assert(abs(sin(i) / cos(i) - tan(i)) < threshold)
end
assert((cosh(.5) - 1.1276259652063807) < threshold)
assert((tanh(0.5) - 1.127625965206) < threshold)
for i in 0..100
assert(abs(sinh(i) / cosh(i) - tanh(i)) < threshold)
end
assert(abs(acos(PI/4) - 0.5) < 0.35)
assert(abs(atan(PI/4) - 0.5) < 0.2)
assert((acos(0.5) - 1.1276259652063807) < threshold)
assert((atan(0.3) - 1.1276259652063807) < threshold)
x = -1; interval = 1000
for i in 0..interval-1
x += 2/interval
assert(abs(sin(asin(x)) - x) < threshold)
assert(abs(cos(acos(x)) - x) < threshold)
assert(abs(tan(atan(x)) - x) < threshold)
end
assert(abs(log10(2) - 0.3010299956639812) < threshold)
assert(round(1.4) == 1)
assert(round(1.5) == 2)
assert(round(-1.5) == -2)
## Note that these mathe functions are removed temproarly from the core
## For more information see PR #201
##
##assert(abs(log2(2) - 1) < threshold)
##assert(abs(log2(1) - 0) < threshold)
##assert(abs(log2(5) - 2.321928094887362) < threshold)
##
##assert(abs(hypot(1,1) - 1.414213562373095) < threshold)
##assert(abs(hypot(3,5) - 5.830951894845301) < threshold)
##
##assert(abs(cbrt(27) - 3) < threshold)
##assert(abs(cbrt(9) - 2.080083823051904) < threshold)
##
##assert(abs(gamma(5) - 24) < threshold)
##assert(abs(gamma(2.2) - 1.101802490879713) < threshold)
# If we got here, that means all test were passed.
print('All TESTS PASSED')

64
tests/modules/math.pk Normal file
View File

@ -0,0 +1,64 @@
from math import *
assert(ceil(1.1) == floor(2.9))
## FIXME:
## temproarly modules cannot define globals via native interface
## and it'll be fixed soon.
PI = 3.14159265358979323846
assert(sin(0) == 0)
assert(sin(PI/2) == 1)
threshold = 0.0000000000001
assert(abs(cos(PI/3) - 0.5) < threshold )
assert(abs(tan(PI/4) - 1.0) < threshold )
for i in 0..1000
assert(abs(sin(i) / cos(i) - tan(i)) < threshold)
end
assert((cosh(.5) - 1.1276259652063807) < threshold)
assert((tanh(0.5) - 1.127625965206) < threshold)
for i in 0..100
assert(abs(sinh(i) / cosh(i) - tanh(i)) < threshold)
end
assert(abs(acos(PI/4) - 0.5) < 0.35)
assert(abs(atan(PI/4) - 0.5) < 0.2)
assert((acos(0.5) - 1.1276259652063807) < threshold)
assert((atan(0.3) - 1.1276259652063807) < threshold)
x = -1; interval = 1000
for i in 0..interval-1
x += 2/interval
assert(abs(sin(asin(x)) - x) < threshold)
assert(abs(cos(acos(x)) - x) < threshold)
assert(abs(tan(atan(x)) - x) < threshold)
end
assert(abs(log10(2) - 0.3010299956639812) < threshold)
assert(round(1.4) == 1)
assert(round(1.5) == 2)
assert(round(-1.5) == -2)
## Note that these mathe functions are removed temproarly from the core
## For more information see PR #201
##
##assert(abs(log2(2) - 1) < threshold)
##assert(abs(log2(1) - 0) < threshold)
##assert(abs(log2(5) - 2.321928094887362) < threshold)
##
##assert(abs(hypot(1,1) - 1.414213562373095) < threshold)
##assert(abs(hypot(3,5) - 5.830951894845301) < threshold)
##
##assert(abs(cbrt(27) - 3) < threshold)
##assert(abs(cbrt(9) - 2.080083823051904) < threshold)
##
##assert(abs(gamma(5) - 24) < threshold)
##assert(abs(gamma(2.2) - 1.101802490879713) < threshold)
# If we got here, that means all test were passed.
print('All TESTS PASSED')

View File

@ -1,7 +1,7 @@
## Example on how to integrate pocket VM with in your application.
- Including this example this repository contains several examples on how to integrate
pocket VM with your application
- Including this example this repository contains several examples on how to
integrate pocket VM with your application
- These examples (currently 2 examples)
- The `cli/` application
- The `docs/try/main.c` web assembly version of pocketlang

View File

@ -10,17 +10,17 @@
// The pocket script we're using to test.
static const char* code =
" from YourModule import variableToC \n"
" a = 42 \n"
" b = variableToC(a) \n"
" print('[pocket] b =', b) \n"
" from my_module import cFunction \n"
" a = 42 \n"
" b = cFunction(a) \n"
" print('[pocket] b = $b') \n"
;
/*****************************************************************************/
/* MODULE FUNCTION */
/*****************************************************************************/
static void variableToC(PKVM* vm) {
static void cFunction(PKVM* vm) {
// Get the parameter from pocket VM.
double a;
@ -36,15 +36,7 @@ static void variableToC(PKVM* vm) {
/* POCKET VM CALLBACKS */
/*****************************************************************************/
// Error report callback.
static void reportError(PKVM* vm, PkErrorType type,
const char* file, int line,
const char* message) {
fprintf(stderr, "Error: %s\n", message);
}
// print() callback to write stdout.
static void stdoutWrite(PKVM* vm, const char* text) {
static void stdoutCallback(PKVM* vm, const char* text) {
fprintf(stdout, "%s", text);
}
@ -56,21 +48,20 @@ int main(int argc, char** argv) {
// Pocket VM configuration.
PkConfiguration config = pkNewConfiguration();
config.error_fn = reportError;
config.write_fn = stdoutWrite;
//config.read_fn = stdinRead;
config.write_fn = stdoutCallback;
// Create a new pocket VM.
PKVM* vm = pkNewVM(&config);
// Register your module.
PkHandle* your_module = pkNewModule(vm, "YourModule");
pkModuleAddFunction(vm, your_module, "variableToC", variableToC, 1);
pkReleaseHandle(vm, your_module);
// Registering a native module.
PkHandle* my_module = pkNewModule(vm, "my_module");
pkModuleAddFunction(vm, my_module, "cFunction", cFunction, 1);
pkRegisterModule(vm, my_module);
pkReleaseHandle(vm, my_module);
// The path and the source code.
PkStringPtr source = { code, NULL, NULL, 0, 0 };
PkStringPtr path = { "./some/path/", NULL, NULL, 0, 0 };
PkStringPtr source = { .string = code };
PkStringPtr path = { .string = "./some/path/" };
// Run the code.
PkResult result = pkInterpretSource(vm, source, path, NULL/*options*/);

View File

@ -3,174 +3,85 @@
* Distributed Under The MIT License
*/
#error Native interface is being refactored and will be completed soon.
// This is an example on how to write your own custom type (Vector here) and
// bind it with with the pocket VM.
#include <pocketlang.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <stdlib.h> /* For malloc */
#include <stdio.h> /* For printf */
#include <string.h> /* For strncmp */
#include <math.h> /* For sqrt */
// The script we're using to test the native Vector type.
static const char* code =
" import Vector # The native module. \n"
" print('Module =', Vector) \n"
" \n"
" vec1 = Vector.new(1, 2) # Calling native method. \n"
" print('vec1 =', 'Vector.new(1, 2)') \n"
" print() \n"
" \n"
" # Using the native getter. \n"
" print('vec1.x =', vec1.x) \n"
" print('vec1.y =', vec1.y) \n"
" print('vec1.length =', vec1.length) \n"
" print() \n"
" \n"
" # Using the native setter. \n"
" vec1.x = 3; vec1.y = 4; \n"
" print('vec1.x =', vec1.x) \n"
" print('vec1.y =', vec1.y) \n"
" print('vec1.length =', vec1.length) \n"
" print() \n"
" \n"
" vec2 = Vector.new(5, 6) \n"
" vec3 = Vector.add(vec1, vec2) \n"
" print('vec3 =', 'Vector.add(vec1, vec2)') \n"
" print('vec3.x =', vec3.x) \n"
" print('vec3.y =', vec3.y) \n"
" \n"
" from vector import Vec2 \n"
" print('Class = $Vec2') \n"
" \n"
" v1 = Vec2(1, 2) \n"
" print('v1 = $v1') \n"
" print() \n"
" \n"
" print('v1.x = ${v1.x}') \n"
" print('v1.y = ${v1.y}') \n"
" print('v1.length = ${v1.length}') \n"
" print() \n"
" \n"
" v1.x = 3; v1.y = 4; \n"
" print('v1.x = ${v1.x}') \n"
" print('v1.y = ${v1.y}') \n"
" print('v1.length = ${v1.length}') \n"
" print() \n"
" \n"
" v2 = Vec2(5, 6) \n"
" v3 = v1.add(v2) \n"
" print('v3 = ${v3}') \n"
" print('v3.x = ${v3.x}') \n"
" print('v3.y = ${v3.y}') \n"
" \n"
;
/*****************************************************************************/
/* NATIVE TYPE DEFINES & CALLBACKS */
/*****************************************************************************/
// An enum value of native object, used as unique of the type in pocketlang.
typedef enum {
OBJ_VECTOR = 0,
} ObjType;
typedef struct {
double x, y; // Vector variables.
} Vector;
// Get name callback, will called from pocketlang to get the type name from
// the ID (the enum value).
const char* getObjName(uint32_t id) {
switch ((ObjType)id) {
case OBJ_VECTOR: return "Vector";
}
return NULL; // Unreachable.
}
// Instance getter callback to get a value from the native instance.
// The hash value and the length of the string are provided with the
// argument [attrib].
void objGetAttrib(PKVM* vm, void* instance, uint32_t id, PkStringPtr attrib) {
switch ((ObjType)id) {
case OBJ_VECTOR: {
Vector* vector = ((Vector*)instance);
if (strcmp(attrib.string, "x") == 0) {
pkReturnNumber(vm, vector->x);
return;
} else if (strcmp(attrib.string, "y") == 0) {
pkReturnNumber(vm, vector->y);
return;
} else if (strcmp(attrib.string, "length") == 0) {
double length = sqrt(pow(vector->x, 2) + pow(vector->y, 2));
pkReturnNumber(vm, length);
return;
}
} break;
}
// If we reached here that means the attribute doesn't exists.
// Since we haven't used pkReturn...() function, pocket VM already
// know that the attribute doesn't exists. just return.
return;
}
// Instance setter callback to set the value to the native instance.
// The hash value and the length of the string are provided with the
// argument [attrib].
bool objSetAttrib(PKVM* vm, void* instance, uint32_t id, PkStringPtr attrib) {
switch ((ObjType)id) {
case OBJ_VECTOR: {
Vector* vector = ((Vector*)instance);
if (strcmp(attrib.string, "x") == 0) {
double x; // Get the number x.
if (!pkGetArgNumber(vm, 0, &x)) return false;
vector->x = x;
return true;
} else if (strcmp(attrib.string, "y") == 0) {
double y; // Get the number x.
if (!pkGetArgNumber(vm, 0, &y)) return false;
vector->y = y;
return true;
}
} break;
}
// If we reached here that means the attribute doesn't exists.
// Return false to indicate it.
return false;
}
// The free object callback, called just before the native instance, garbage
// collect.
void freeObj(PKVM* vm, void* instance, uint32_t id) {
free((void*)instance); // Your cleanups.
}
/*****************************************************************************/
/* VECTOR MODULE FUNCTIONS REGISTER */
/*****************************************************************************/
// The Vector.new(x, y) function.
void _vecNew(PKVM* vm) {
double x, y; // The args.
// Get the args from the stack, If it's not number, return.
if (!pkGetArgNumber(vm, 1, &x)) return;
if (!pkGetArgNumber(vm, 2, &y)) return;
// Create a new vector.
typedef struct {
double x, y;
} Vector;
// Native instance allocation callback.
void* _newVec() {
Vector* vec = (Vector*)malloc(sizeof(Vector));
vec->x = x, vec->y = y;
pkReturnInstNative(vm, (void*)vec, OBJ_VECTOR);
vec->x = 0;
vec->y = 0;
return vec;
}
// The Vector.length(vec) function.
void _vecAdd(PKVM* vm) {
Vector *v1, *v2;
if (!pkGetArgInst(vm, 1, OBJ_VECTOR, (void**)&v1)) return;
if (!pkGetArgInst(vm, 2, OBJ_VECTOR, (void**)&v2)) return;
// Create a new vector.
Vector* v3 = (Vector*)malloc(sizeof(Vector));
v3->x = v1->x + v2->x;
v3->y = v1->y + v2->y;
// Native instance de-allocatoion callback.
void _deleteVec(void* vector) {
free(vector);
}
pkReturnInstNative(vm, (void*)v3, OBJ_VECTOR);
// Vec2 'add' method.
void _vec2Add(PKVM* vm) {
Vector* self = (Vector*)pkGetSelf(vm);
// FIXME:
// Temproarly it's not possible to get vector from the args since the native
// interface is being refactored. Will be implemented soon.
}
// Register the 'Vector' module and it's functions.
void registerVector(PKVM* vm) {
PkHandle* vector = pkNewModule(vm, "Vector");
PkHandle* vector = pkNewModule(vm, "vector");
pkModuleAddFunction(vm, vector, "new", _vecNew, 2);
pkModuleAddFunction(vm, vector, "add", _vecAdd, 2);
PkHandle* Vec2 = pkNewClass(vm, "Vec2", NULL /*Base Class*/,
vector, _newVec, _deleteVec);
pkClassAddMethod(vm, Vec2, "add", _vec2Add, 1);
pkReleaseHandle(vm, Vec2);
pkRegisterModule(vm, vector);
pkReleaseHandle(vm, vector);
}
@ -178,35 +89,20 @@ void registerVector(PKVM* vm) {
/* POCKET VM CALLBACKS */
/*****************************************************************************/
// Error report callback.
void reportError(PKVM* vm, PkErrorType type,
const char* file, int line,
const char* message) {
fprintf(stderr, "Error: %s\n", message);
}
// print() callback to write stdout.
void stdoutWrite(PKVM* vm, const char* text) {
void stdoutCallback(PKVM* vm, const char* text) {
fprintf(stdout, "%s", text);
}
int main(int argc, char** argv) {
PkConfiguration config = pkNewConfiguration();
config.error_fn = reportError;
config.write_fn = stdoutWrite;
//config.read_fn = stdinRead;
config.inst_free_fn = freeObj;
config.inst_name_fn = getObjName;
config.inst_get_attrib_fn = objGetAttrib;
config.inst_set_attrib_fn = objSetAttrib;
config.write_fn = stdoutCallback;
PKVM* vm = pkNewVM(&config);
registerVector(vm);
PkStringPtr source = { code, NULL, NULL, 0, 0 };
PkStringPtr path = { "./some/path/", NULL, NULL, 0, 0 };
PkStringPtr source = { .string = code };
PkStringPtr path = { .string = "./some/path/" };
PkResult result = pkInterpretSource(vm, source, path, NULL/*options*/);
pkFreeVM(vm);

View File

@ -0,0 +1,67 @@
class Node
def _init(val)
self.val = val
self.next = null
end
def _to_string()
return "(${self.val})"
end
end
class LinkedList
def _init()
self.head = null
end
def append(node)
if self.head == null
self.head = node
else
last = self.head
while last.next
last = last.next
end
last.next = node
end
end
def reverse()
curr = self.head
prev = null; next = null
while curr
next = curr.next
curr.next = prev
prev = curr
curr = next
end
self.head = prev
end
def _to_string()
ret = ""
next = self.head
while next
ret += next._to_string()
ret += " --> "
next = next.next
end
ret += "null"
return ret
end
end
ll = LinkedList()
ll.append(Node(4))
ll.append(Node(6))
ll.append(Node(3))
ll.append(Node(9))
## FIXME: No override supported at the moment.
print(ll._to_string())
ll.reverse()
print(ll._to_string())

View File

@ -65,7 +65,7 @@ def eval(expr, ind)
return [r[1] / r[2], r[3]]
else if isnum(c)
val = str_ord(c) - str_ord('0')
val = ord(c) - ord('0')
assert(0 <= val and val < 10)
return [val, ind]
@ -93,7 +93,7 @@ end
## Return true if c in numeric.
def isnum(c)
k = str_ord(c) - str_ord('0')
k = ord(c) - ord('0')
## TODO: k in 0..10
return (0 <= k and k < 10)
end

View File

@ -26,8 +26,13 @@ TEST_SUITE = {
"lang/functions.pk",
"lang/import.pk",
),
"Modules Test" : (
"modules/math.pk",
),
"Random Scripts" : (
"random/linked_list.pk",
"random/lisp_eval.pk",
"random/string_algo.pk",
),