Adds module support to trainscript. Moves global methods to /sys/* objects.
This commit is contained in:
parent
81ad6c3406
commit
d0d88a17f3
12 changed files with 187 additions and 51 deletions
22
Makefile
22
Makefile
|
@ -87,9 +87,9 @@ obj/vmm.o: src/vmm.c include/config.h include/vmm.h include/pmm.h \
|
|||
# trainscript/tsvm.cpp
|
||||
obj/tsvm.o: trainscript/tsvm.cpp include/stdlib.h include/varargs.h \
|
||||
include/config.h include/malloc.h include/console.h trainscript/common.h \
|
||||
trainscript/tsvm.hpp trainscript/vm.hpp trainscript/module.hpp \
|
||||
include/ker/string.hpp include/ker/dictionary.hpp include/kernel.h \
|
||||
include/ker/pair.hpp include/ker/vector.hpp include/ker/new.hpp \
|
||||
include/ker/vector.hpp include/ker/new.hpp include/ker/dictionary.hpp \
|
||||
include/kernel.h include/ker/pair.hpp trainscript/tsvm.hpp \
|
||||
trainscript/vm.hpp trainscript/module.hpp include/ker/string.hpp \
|
||||
trainscript/variable.hpp trainscript/type.hpp trainscript/types.hpp \
|
||||
trainscript/typeid.hpp trainscript/method.hpp \
|
||||
trainscript/instructions.hpp trainscript/instruction.hpp \
|
||||
|
@ -134,10 +134,10 @@ obj/vm.o: src/vm.cpp include/stdlib.h include/varargs.h include/config.h \
|
|||
# obj/trainscript.yy.cpp
|
||||
obj/trainscript.yy.o: obj/trainscript.yy.cpp include/string.h \
|
||||
include/stdlib.h include/varargs.h include/config.h include/malloc.h \
|
||||
trainscript/common.h trainscript/tsvm.hpp trainscript/vm.hpp \
|
||||
trainscript/module.hpp include/ker/string.hpp include/ker/dictionary.hpp \
|
||||
include/kernel.h include/ker/pair.hpp include/ker/vector.hpp \
|
||||
include/ker/new.hpp trainscript/variable.hpp trainscript/type.hpp \
|
||||
trainscript/common.h include/ker/vector.hpp include/ker/new.hpp \
|
||||
include/ker/dictionary.hpp include/kernel.h include/ker/pair.hpp \
|
||||
trainscript/tsvm.hpp trainscript/vm.hpp trainscript/module.hpp \
|
||||
include/ker/string.hpp trainscript/variable.hpp trainscript/type.hpp \
|
||||
trainscript/types.hpp trainscript/typeid.hpp trainscript/method.hpp \
|
||||
trainscript/instructions.hpp trainscript/instruction.hpp \
|
||||
trainscript/executioncontext.hpp trainscript/scriptmethod.hpp \
|
||||
|
@ -147,10 +147,10 @@ obj/trainscript.yy.o: obj/trainscript.yy.cpp include/string.h \
|
|||
# obj/trainscript.tab.cpp
|
||||
obj/trainscript.tab.o: obj/trainscript.tab.cpp include/stdlib.h \
|
||||
include/varargs.h include/config.h include/malloc.h include/console.h \
|
||||
trainscript/common.h trainscript/tsvm.hpp trainscript/vm.hpp \
|
||||
trainscript/module.hpp include/ker/string.hpp include/ker/dictionary.hpp \
|
||||
include/kernel.h include/ker/pair.hpp include/ker/vector.hpp \
|
||||
include/ker/new.hpp trainscript/variable.hpp trainscript/type.hpp \
|
||||
trainscript/common.h include/ker/vector.hpp include/ker/new.hpp \
|
||||
include/ker/dictionary.hpp include/kernel.h include/ker/pair.hpp \
|
||||
trainscript/tsvm.hpp trainscript/vm.hpp trainscript/module.hpp \
|
||||
include/ker/string.hpp trainscript/variable.hpp trainscript/type.hpp \
|
||||
trainscript/types.hpp trainscript/typeid.hpp trainscript/method.hpp \
|
||||
trainscript/instructions.hpp trainscript/instruction.hpp \
|
||||
trainscript/executioncontext.hpp trainscript/scriptmethod.hpp \
|
||||
|
|
19
README.md
19
README.md
|
@ -12,6 +12,7 @@ Also it leaks memory. A lot of memory.
|
|||
- Fixing Memory Leaks
|
||||
- Adding support for Modules
|
||||
- Adding support for Pointer Types
|
||||
-
|
||||
|
||||
## Guidlines
|
||||
- Calls to `die` or `die_extra` should follow the following scheme: `ContextName.ErrorName`
|
||||
|
@ -69,6 +70,24 @@ The virtual machine supports 5 base types:
|
|||
### Variable Format
|
||||
Variables are stored in a `type+pointer` format where `type` stores the type of the variable and `pointer` either a pointer to the variable value or, if `type` is a pointer type, the pointer value of the variable.
|
||||
|
||||
### Native Interoperation
|
||||
The virtual machine features a `NativeMethod` that allows calling a C function (`result_t __cdecl name(args...)`) from the
|
||||
virtual machine. Parameters are passed as the VM types declared in `types.hpp`, except for ker::String which is passed as a `const char*`.
|
||||
|
||||
### Native Modules
|
||||
The kernel provides native modules that can be used to interoperate with the kernel and the system hardware.
|
||||
|
||||
Following modules are planned:
|
||||
|
||||
#### Timer
|
||||
The timer module provides interaction with the system timer.
|
||||
|
||||
#### Memory
|
||||
The memory module allows allocating and freeing memory as well as getting predefined named memory areas.
|
||||
|
||||
#### Interrupts
|
||||
The interrupt module allows handling interrupts by the current module.
|
||||
|
||||
|
||||
## OS Architecture
|
||||
To be done.
|
||||
|
|
|
@ -66,6 +66,11 @@ namespace ker
|
|||
return memcmp(this->mText, other.mText, this->mLength) == 0;
|
||||
}
|
||||
|
||||
bool equals(const char *other) const
|
||||
{
|
||||
return strcmp(this->str(), other) == 0;
|
||||
}
|
||||
|
||||
String append(const String &other) const
|
||||
{
|
||||
uint8_t *data = (uint8_t*)malloc(this->mLength + other.mLength);
|
||||
|
@ -122,11 +127,21 @@ namespace ker
|
|||
return this->equals(other);
|
||||
}
|
||||
|
||||
bool operator ==(const char *other) const
|
||||
{
|
||||
return this->equals(other);
|
||||
}
|
||||
|
||||
bool operator !=(const String &other) const
|
||||
{
|
||||
return !this->equals(other);
|
||||
}
|
||||
|
||||
bool operator !=(const char *other) const
|
||||
{
|
||||
return !this->equals(other);
|
||||
}
|
||||
|
||||
String operator +(const String &other) const
|
||||
{
|
||||
return this->append(other);
|
||||
|
|
|
@ -1,24 +1,25 @@
|
|||
VAR global : INT;
|
||||
|
||||
# OBJ timer : "/sys/timer";
|
||||
# OBJ heap : "/sys/malloc";
|
||||
# OBJ interrupts : "/sys/interrupt";
|
||||
OBJ timer : "/sys/timer";
|
||||
OBJ console : "/sys/console";
|
||||
|
||||
VAR ptr : PTR(INT);
|
||||
|
||||
PUB main() | i : INT, str : TEXT
|
||||
BEGIN
|
||||
0 -> i;
|
||||
"Hello " -> str;
|
||||
printStr(str + "World!");
|
||||
console.printStr(str + "World!");
|
||||
WHILE ((i + 1) -> i) <= fun() DO
|
||||
BEGIN
|
||||
hlp(i, fun() - i);
|
||||
sleep(2);
|
||||
timer.sleep(2);
|
||||
END
|
||||
END
|
||||
|
||||
PRI hlp(i : INT, j : INT)
|
||||
BEGIN
|
||||
print2Int(i, j);
|
||||
console.print2Int(i, j);
|
||||
END
|
||||
|
||||
PUB fun() -> i : INT
|
||||
|
|
51
src/vm.cpp
51
src/vm.cpp
|
@ -141,16 +141,45 @@ struct NativeModuleDef
|
|||
void *function;
|
||||
};
|
||||
|
||||
NativeModuleDef methods[] = {
|
||||
{ "sleep", "i", (void*)sleep },
|
||||
{ "timer_get", "", (void*)timer_get },
|
||||
{ "timer_set", "i", (void*)timer_set },
|
||||
NativeModuleDef consoleModule[] = {
|
||||
{ "printInt", "i", (void*)printInt },
|
||||
{ "printStr", "t", (void*)printStr },
|
||||
{ "print2Int", "ii", (void*)print2Int },
|
||||
{ nullptr, nullptr, 0 }
|
||||
};
|
||||
|
||||
static NativeModuleDef timerModule[] = {
|
||||
{ "sleep", "i", (void*)sleep },
|
||||
{ "timer_get", "", (void*)timer_get },
|
||||
{ "timer_set", "i", (void*)timer_set },
|
||||
{ nullptr, nullptr, 0 }
|
||||
};
|
||||
|
||||
class KernelVM : public VM
|
||||
{
|
||||
public:
|
||||
Module *createNative(NativeModuleDef *mod)
|
||||
{
|
||||
Module *module = new Module();
|
||||
while(mod->name != nullptr) {
|
||||
module->methods.add(mod->name, new NativeMethod(mod->signature, mod->function));
|
||||
mod++;
|
||||
}
|
||||
return module;
|
||||
}
|
||||
|
||||
Module *create(const ker::String &name) override
|
||||
{
|
||||
if(name == "/sys/timer") {
|
||||
return createNative(timerModule);
|
||||
}
|
||||
if(name == "/sys/console") {
|
||||
return createNative(consoleModule);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
extern "C" void vm_start()
|
||||
{
|
||||
struct {
|
||||
|
@ -164,7 +193,10 @@ extern "C" void vm_start()
|
|||
// cpp_test();
|
||||
|
||||
kprintf("Parse kernel module: ");
|
||||
Module *module = VM::load(mainfile.ptr, mainfile.size);
|
||||
|
||||
KernelVM vm;
|
||||
|
||||
Module *module = vm.load(mainfile.ptr, mainfile.size);
|
||||
if(module == nullptr) {
|
||||
kprintf("Could not load module :(\n");
|
||||
return;
|
||||
|
@ -172,15 +204,6 @@ extern "C" void vm_start()
|
|||
|
||||
kprintf("Module successfully loaded :)\n");
|
||||
|
||||
|
||||
|
||||
// Load native modules
|
||||
NativeModuleDef *mod = methods;
|
||||
while(mod->name != nullptr) {
|
||||
module->methods.add(mod->name, new NativeMethod(mod->signature, mod->function));
|
||||
mod++;
|
||||
}
|
||||
|
||||
String errorCode;
|
||||
if(module->validate(errorCode) == false) {
|
||||
kprintf("Module validation failed: \x12\x06%s\x12\x07\n", errorCode.str());
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
#include <stddef.h>
|
||||
#include <inttypes.h>
|
||||
#include <ker/vector.hpp>
|
||||
#include <ker/dictionary.hpp>
|
||||
|
||||
#include "tsvm.hpp"
|
||||
|
||||
|
@ -28,20 +30,19 @@ struct ParserData
|
|||
size_t length;
|
||||
trainscript::Module *module;
|
||||
void *scanner;
|
||||
char* strings[256];
|
||||
ker::Vector<char*> strings;
|
||||
ker::Dictionary<ker::String, ker::String> objects;
|
||||
|
||||
char *strdup(const char *str)
|
||||
{
|
||||
for(size_t i = 0; i < 256; i++) {
|
||||
if(this->strings[i] == nullptr) {
|
||||
return this->strings[i] = ::strdup(str);
|
||||
}
|
||||
else if(strcmp(this->strings[i], str) == 0) {
|
||||
for(size_t i = 0; i < this->strings.length(); i++) {
|
||||
if(strcmp(this->strings[i], str) == 0) {
|
||||
return this->strings[i];
|
||||
}
|
||||
}
|
||||
die_extra("ParserData::strdup", "out of strings");
|
||||
return nullptr;
|
||||
char *nstr = ::strdup(str);
|
||||
this->strings.append(nstr);
|
||||
return nstr;
|
||||
}
|
||||
|
||||
};
|
||||
|
@ -52,6 +53,12 @@ struct VariableDeclaration
|
|||
trainscript::Type type;
|
||||
};
|
||||
|
||||
struct ObjectDeclaration
|
||||
{
|
||||
char *name;
|
||||
char *moduleName;
|
||||
};
|
||||
|
||||
struct LocalVariable
|
||||
{
|
||||
char *name;
|
||||
|
|
|
@ -160,10 +160,12 @@ namespace trainscript
|
|||
public Instruction
|
||||
{
|
||||
public:
|
||||
ker::String moduleName;
|
||||
ker::String methodName;
|
||||
ker::Vector<Instruction*> parameters;
|
||||
|
||||
MethodInvokeExpression(ker::String methodName) :
|
||||
MethodInvokeExpression(const ker::String moduleName, const ker::String &methodName) :
|
||||
moduleName(moduleName),
|
||||
methodName(methodName)
|
||||
{
|
||||
|
||||
|
@ -171,7 +173,17 @@ namespace trainscript
|
|||
|
||||
Variable execute(ExecutionContext &context) const override
|
||||
{
|
||||
Method *method = context.module->method(this->methodName.str());
|
||||
Module *module = nullptr;
|
||||
if((this->moduleName.length() == 0) || (this->moduleName == ker::String("this"))) {
|
||||
module = context.module;
|
||||
} else {
|
||||
module = context.module->object(this->moduleName);
|
||||
}
|
||||
if(module == nullptr) {
|
||||
die_extra("MethodInvokeExpression.InvalidModule", this->moduleName.str());
|
||||
}
|
||||
|
||||
Method *method = module->method(this->methodName.str());
|
||||
if(method == nullptr) {
|
||||
die_extra("MethodInvokeExpression.MethodNotFound", this->methodName.str());
|
||||
}
|
||||
|
@ -186,7 +198,15 @@ namespace trainscript
|
|||
}
|
||||
|
||||
bool validate(ExecutionContext &context, ker::String &errorCode) const override {
|
||||
Method *method = context.module->method(this->methodName.str());
|
||||
|
||||
Module *module = nullptr;
|
||||
if((this->moduleName.length() == 0) || (this->moduleName == ker::String("this"))) {
|
||||
module = context.module;
|
||||
} else {
|
||||
module = context.module->object(this->moduleName);
|
||||
}
|
||||
|
||||
Method *method = module->method(this->methodName.str());
|
||||
if(method == nullptr) {
|
||||
errorCode = "The method " + this->methodName + " does not exist.";
|
||||
return false;
|
||||
|
|
|
@ -13,16 +13,22 @@ namespace trainscript
|
|||
public:
|
||||
ker::Dictionary<ker::String, Variable*> variables;
|
||||
ker::Dictionary<ker::String, Method*> methods;
|
||||
ker::Dictionary<ker::String, Module*> objects;
|
||||
public:
|
||||
Module();
|
||||
~Module();
|
||||
|
||||
Method *method(const char *name)
|
||||
Module *object(const ker::String &name)
|
||||
{
|
||||
return this->objects.get(name);
|
||||
}
|
||||
|
||||
Method *method(const ker::String &name)
|
||||
{
|
||||
return this->methods.get(name);
|
||||
}
|
||||
|
||||
Variable *variable(const char *name)
|
||||
Variable *variable(const ker::String &name)
|
||||
{
|
||||
return this->variables.get(name);
|
||||
}
|
||||
|
|
|
@ -40,6 +40,7 @@ VAR { return KW_VAR; }
|
|||
PUB { return KW_PUB; }
|
||||
PRI { return KW_PRI; }
|
||||
PTR { return KW_PTR; }
|
||||
OBJ { return KW_OBJ; }
|
||||
|
||||
VOID { return KW_VOID; }
|
||||
INT { return KW_INT; }
|
||||
|
@ -67,6 +68,7 @@ DO { return KW_DO; }
|
|||
[0-9]+\.[0-9]* { yylval->fval = atof(yytext); return REAL; }
|
||||
[0-9]+ { yylval->ival = atoi(yytext); return INT; }
|
||||
[a-zA-Z0-9'_]+ { yylval->text = yyextra->strdup(yytext); return IDENTIFIER; }
|
||||
\. { return DOT; }
|
||||
. { yyerror(NULL, "illegal token"); }
|
||||
%%
|
||||
/*
|
||||
|
|
|
@ -32,6 +32,7 @@ void yyerror(void *scanner, const char *s);
|
|||
int indentation;
|
||||
trainscript::Type type;
|
||||
VariableDeclaration varDecl;
|
||||
ObjectDeclaration objDecl;
|
||||
MethodDeclaration method;
|
||||
MethodBody *body;
|
||||
MethodHeader methodHeader;
|
||||
|
@ -44,6 +45,7 @@ void yyerror(void *scanner, const char *s);
|
|||
%token TYPENAME
|
||||
%token SEMICOLON
|
||||
%token COLON
|
||||
%token DOT
|
||||
%token COMMA
|
||||
%token PIPE
|
||||
%token PLUS
|
||||
|
@ -71,6 +73,7 @@ void yyerror(void *scanner, const char *s);
|
|||
%token KW_PUB
|
||||
%token KW_PRI
|
||||
%token KW_VAR
|
||||
%token KW_OBJ
|
||||
%token KW_PTR
|
||||
|
||||
%token KW_VOID
|
||||
|
@ -95,7 +98,7 @@ void yyerror(void *scanner, const char *s);
|
|||
|
||||
%type <type> typeName
|
||||
%type <varDecl> variableDeclaration
|
||||
|
||||
%type <objDecl> objectDeclaration
|
||||
// %type <indentation> indentation
|
||||
|
||||
%type <method> method
|
||||
|
@ -132,6 +135,9 @@ input:
|
|||
auto *var = new Variable($2.type.createInstance());
|
||||
context->module->variables.add( ker::String($2.name), var );
|
||||
}
|
||||
| input objectDeclaration SEMICOLON {
|
||||
context->objects.add($2.name, $2.moduleName);
|
||||
}
|
||||
| input method {
|
||||
using namespace trainscript;
|
||||
auto *mod = context->module;
|
||||
|
@ -323,7 +329,7 @@ expression:
|
|||
| TEXT { $$ = new ConstantExpression(Variable::fromText($1)); }
|
||||
| IDENTIFIER { $$ = new VariableExpression($1); }
|
||||
| IDENTIFIER LBRACKET expressionList RBRACKET {
|
||||
auto *call = new MethodInvokeExpression($1);
|
||||
auto *call = new MethodInvokeExpression("", $1);
|
||||
auto *list = $3;
|
||||
while(list) {
|
||||
call->parameters.append(list->instruction);
|
||||
|
@ -331,6 +337,15 @@ expression:
|
|||
}
|
||||
$$ = call;
|
||||
}
|
||||
| IDENTIFIER DOT IDENTIFIER LBRACKET expressionList RBRACKET {
|
||||
auto *call = new MethodInvokeExpression($1, $3);
|
||||
auto *list = $5;
|
||||
while(list) {
|
||||
call->parameters.append(list->instruction);
|
||||
list = list->next;
|
||||
}
|
||||
$$ = call;
|
||||
}
|
||||
| LBRACKET expression RBRACKET { $$ = $2; }
|
||||
| expression PLUS expression { $$ = new ArithmeticExpression($1, $3, Operation::Add); }
|
||||
| expression MINUS expression { $$ = new ArithmeticExpression($1, $3, Operation::Subtract); }
|
||||
|
@ -373,6 +388,13 @@ variableDeclaration:
|
|||
}
|
||||
;
|
||||
|
||||
objectDeclaration:
|
||||
KW_OBJ IDENTIFIER COLON TEXT {
|
||||
$$.name = $2;
|
||||
$$.moduleName = $4;
|
||||
}
|
||||
;
|
||||
|
||||
typeName:
|
||||
KW_VOID { $$ = Type::Void; }
|
||||
| KW_INT { $$ = Type::Int; }
|
||||
|
|
|
@ -21,6 +21,11 @@ namespace trainscript
|
|||
return true;
|
||||
}
|
||||
|
||||
Module *VM::create(const ker::String &)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Module *VM::load(const void *buffer, size_t length)
|
||||
{
|
||||
char *internalStorage = (char*)malloc(length);
|
||||
|
@ -41,10 +46,19 @@ namespace trainscript
|
|||
|
||||
free(internalStorage);
|
||||
|
||||
for(size_t i = 0; i < 256; i++) {
|
||||
if(data.strings[i] != nullptr) {
|
||||
free(data.strings[i]);
|
||||
for(char *ptr : data.strings) {
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
for(const ker::Pair<ker::String, ker::String> &mod : data.objects)
|
||||
{
|
||||
Module *obj = this->create(mod.second);
|
||||
if(obj == nullptr) {
|
||||
kprintf("Module \"%s\" not found.\n", mod.second.str());
|
||||
delete module;
|
||||
return nullptr;
|
||||
}
|
||||
module->objects.add(mod.first, obj);
|
||||
}
|
||||
|
||||
if(valid) {
|
||||
|
|
|
@ -7,8 +7,15 @@ namespace trainscript
|
|||
class VM
|
||||
{
|
||||
public:
|
||||
static Module *load(const void *buffer, size_t length);
|
||||
Module *load(const void *buffer, size_t length);
|
||||
|
||||
static Module *load(const char *text);
|
||||
Module *load(const char *text);
|
||||
|
||||
/**
|
||||
* @brief Creates a module by the module name.
|
||||
* @param name The name of the module
|
||||
* @return New module or nullptr if the named module does not exist.
|
||||
*/
|
||||
virtual Module *create(const ker::String &name);
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue