Adds module support to trainscript. Moves global methods to /sys/* objects.

This commit is contained in:
Felix Queissner 2015-08-21 17:32:48 +02:00
parent 81ad6c3406
commit d0d88a17f3
12 changed files with 187 additions and 51 deletions

View file

@ -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 \

View file

@ -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.

View file

@ -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);

View file

@ -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

View file

@ -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());

View file

@ -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;

View file

@ -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;

View file

@ -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);
}

View file

@ -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"); }
%%
/*

View file

@ -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; }

View file

@ -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) {

View file

@ -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);
};
}