Refactors tsvm.hpp into more subfiles for better structure.
This commit is contained in:
parent
c21e7e7f30
commit
5f168f8020
21 changed files with 780 additions and 815 deletions
80
Makefile
80
Makefile
|
@ -35,94 +35,112 @@ kernel: obj/tsvm.o obj/dynamic.o obj/intr_common_handler.o obj/multiboot.o obj/s
|
|||
# src/console.c
|
||||
obj/console.o: src/console.c include/console.h include/stdlib.h \
|
||||
include/varargs.h include/config.h include/malloc.h
|
||||
$(CC) $(FLAGS) $(CCFLAGS) -o $@ -c src/console.c
|
||||
$(CC) -iquoteobj $(FLAGS) $(CCFLAGS) -o $@ -c src/console.c
|
||||
|
||||
# src/init.c
|
||||
obj/init.o: src/init.c include/kernel.h include/stdlib.h include/varargs.h \
|
||||
include/config.h include/malloc.h include/console.h include/interrupts.h \
|
||||
include/cpustate.h include/pmm.h include/multiboot.h include/vmm.h \
|
||||
include/timer.h include/serial.h
|
||||
$(CC) $(FLAGS) $(CCFLAGS) -o $@ -c src/init.c
|
||||
$(CC) -iquoteobj $(FLAGS) $(CCFLAGS) -o $@ -c src/init.c
|
||||
|
||||
# src/interrupts.c
|
||||
obj/interrupts.o: src/interrupts.c include/interrupts.h include/cpustate.h \
|
||||
include/console.h include/stdlib.h include/varargs.h include/config.h \
|
||||
include/malloc.h include/io.h src/intr_stubs.h
|
||||
$(CC) $(FLAGS) $(CCFLAGS) -o $@ -c src/interrupts.c
|
||||
$(CC) -iquoteobj $(FLAGS) $(CCFLAGS) -o $@ -c src/interrupts.c
|
||||
|
||||
# src/malloc.c
|
||||
obj/malloc.o: src/malloc.c include/kernel.h include/stdlib.h \
|
||||
include/varargs.h include/config.h include/malloc.h include/console.h \
|
||||
include/serial.h
|
||||
$(CC) $(FLAGS) $(CCFLAGS) -o $@ -c src/malloc.c
|
||||
$(CC) -iquoteobj $(FLAGS) $(CCFLAGS) -o $@ -c src/malloc.c
|
||||
|
||||
# src/pmm.c
|
||||
obj/pmm.o: src/pmm.c include/pmm.h include/multiboot.h include/kernel.h \
|
||||
include/stdlib.h include/varargs.h include/config.h include/malloc.h \
|
||||
include/console.h
|
||||
$(CC) $(FLAGS) $(CCFLAGS) -o $@ -c src/pmm.c
|
||||
$(CC) -iquoteobj $(FLAGS) $(CCFLAGS) -o $@ -c src/pmm.c
|
||||
|
||||
# src/serial.c
|
||||
obj/serial.o: src/serial.c include/io.h include/serial.h include/stdlib.h \
|
||||
include/varargs.h include/config.h include/malloc.h
|
||||
$(CC) $(FLAGS) $(CCFLAGS) -o $@ -c src/serial.c
|
||||
$(CC) -iquoteobj $(FLAGS) $(CCFLAGS) -o $@ -c src/serial.c
|
||||
|
||||
# src/stdlib.c
|
||||
obj/stdlib.o: src/stdlib.c include/stdlib.h include/varargs.h \
|
||||
include/config.h include/malloc.h include/kernel.h
|
||||
$(CC) $(FLAGS) $(CCFLAGS) -o $@ -c src/stdlib.c
|
||||
$(CC) -iquoteobj $(FLAGS) $(CCFLAGS) -o $@ -c src/stdlib.c
|
||||
|
||||
# src/timer.c
|
||||
obj/timer.o: src/timer.c include/timer.h include/kernel.h \
|
||||
include/interrupts.h include/cpustate.h
|
||||
$(CC) $(FLAGS) $(CCFLAGS) -o $@ -c src/timer.c
|
||||
$(CC) -iquoteobj $(FLAGS) $(CCFLAGS) -o $@ -c src/timer.c
|
||||
|
||||
# src/vmm.c
|
||||
obj/vmm.o: src/vmm.c include/config.h include/vmm.h include/pmm.h \
|
||||
include/multiboot.h include/stdlib.h include/varargs.h include/malloc.h \
|
||||
include/console.h include/kernel.h
|
||||
$(CC) $(FLAGS) $(CCFLAGS) -o $@ -c src/vmm.c
|
||||
$(CC) -iquoteobj $(FLAGS) $(CCFLAGS) -o $@ -c src/vmm.c
|
||||
|
||||
# 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 include/ker/string.hpp include/ker/vector.hpp \
|
||||
include/ker/new.hpp include/ker/dictionary.hpp include/kernel.h \
|
||||
include/ker/pair.hpp trainscript/typeid.hpp \
|
||||
trainscript/trainscript.tab.hpp trainscript/trainscript.l.h \
|
||||
include/string.h
|
||||
$(CXX) $(FLAGS) $(CXXFLAGS) -o $@ -c trainscript/tsvm.cpp
|
||||
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/types.hpp \
|
||||
trainscript/typeid.hpp trainscript/method.hpp \
|
||||
trainscript/instructions.hpp trainscript/instruction.hpp \
|
||||
trainscript/executioncontext.hpp trainscript/scriptmethod.hpp \
|
||||
obj/trainscript.tab.hpp trainscript/trainscript.l.h include/string.h
|
||||
$(CXX) -iquoteobj $(FLAGS) $(CXXFLAGS) -o $@ -c trainscript/tsvm.cpp
|
||||
|
||||
# src/cplusplus.cpp
|
||||
obj/cplusplus.o: src/cplusplus.cpp include/stdlib.h include/varargs.h \
|
||||
include/config.h include/malloc.h include/console.h include/ker/new.hpp
|
||||
$(CXX) $(FLAGS) $(CXXFLAGS) -o $@ -c src/cplusplus.cpp
|
||||
$(CXX) -iquoteobj $(FLAGS) $(CXXFLAGS) -o $@ -c src/cplusplus.cpp
|
||||
|
||||
# src/vm.cpp
|
||||
obj/vm.o: src/vm.cpp include/stdlib.h include/varargs.h include/config.h \
|
||||
include/malloc.h include/timer.h include/dynamic.h \
|
||||
src/../trainscript/tsvm.hpp include/console.h include/ker/string.hpp \
|
||||
include/ker/vector.hpp include/ker/new.hpp include/ker/dictionary.hpp \
|
||||
include/kernel.h include/ker/pair.hpp src/../trainscript/typeid.hpp
|
||||
$(CXX) $(FLAGS) $(CXXFLAGS) -o $@ -c src/vm.cpp
|
||||
include/malloc.h include/timer.h include/dynamic.h include/console.h \
|
||||
src/../trainscript/tsvm.hpp src/../trainscript/vm.hpp \
|
||||
src/../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 \
|
||||
src/../trainscript/variable.hpp src/../trainscript/type.hpp \
|
||||
src/../trainscript/types.hpp src/../trainscript/typeid.hpp \
|
||||
src/../trainscript/method.hpp src/../trainscript/instructions.hpp \
|
||||
src/../trainscript/instruction.hpp \
|
||||
src/../trainscript/executioncontext.hpp \
|
||||
src/../trainscript/scriptmethod.hpp
|
||||
$(CXX) -iquoteobj $(FLAGS) $(CXXFLAGS) -o $@ -c src/vm.cpp
|
||||
|
||||
# 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 include/console.h \
|
||||
include/ker/string.hpp include/ker/vector.hpp include/ker/new.hpp \
|
||||
include/ker/dictionary.hpp include/kernel.h include/ker/pair.hpp \
|
||||
trainscript/typeid.hpp obj/trainscript.tab.hpp
|
||||
$(CXX) -iquotetrainscript $(FLAGS) $(CXXFLAGS) -o $@ -c obj/trainscript.yy.cpp
|
||||
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/types.hpp trainscript/typeid.hpp trainscript/method.hpp \
|
||||
trainscript/instructions.hpp trainscript/instruction.hpp \
|
||||
trainscript/executioncontext.hpp trainscript/scriptmethod.hpp \
|
||||
obj/trainscript.tab.hpp
|
||||
$(CXX) -iquotetrainscript -iquoteobj $(FLAGS) $(CXXFLAGS) -o $@ -c obj/trainscript.yy.cpp
|
||||
|
||||
# obj/trainscript.tab.cpp
|
||||
obj/trainscript.tab.o: obj/trainscript.tab.cpp include/stdlib.h \
|
||||
include/varargs.h include/config.h include/malloc.h trainscript/common.h \
|
||||
trainscript/tsvm.hpp include/console.h include/ker/string.hpp \
|
||||
include/ker/vector.hpp include/ker/new.hpp include/ker/dictionary.hpp \
|
||||
include/kernel.h include/ker/pair.hpp trainscript/typeid.hpp \
|
||||
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/types.hpp trainscript/typeid.hpp trainscript/method.hpp \
|
||||
trainscript/instructions.hpp trainscript/instruction.hpp \
|
||||
trainscript/executioncontext.hpp trainscript/scriptmethod.hpp \
|
||||
trainscript/trainscript.l.h include/string.h
|
||||
$(CXX) -iquotetrainscript $(FLAGS) $(CXXFLAGS) -o $@ -c obj/trainscript.tab.cpp
|
||||
$(CXX) -iquotetrainscript -iquoteobj $(FLAGS) $(CXXFLAGS) -o $@ -c obj/trainscript.tab.cpp
|
||||
|
||||
# asm/dynamic.S
|
||||
obj/dynamic.o: asm/dynamic.S
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
VAR global : INT;
|
||||
|
||||
# OBJ timer : "/sys/timer";
|
||||
# OBJ heap : "/sys/malloc";
|
||||
# OBJ interrupts : "/sys/interrupt";
|
||||
|
||||
PUB main() | i : INT
|
||||
BEGIN
|
||||
0 -> i;
|
||||
|
@ -12,5 +16,5 @@ END
|
|||
|
||||
PUB fun() -> i : INT
|
||||
BEGIN
|
||||
30 -> i;
|
||||
60 -> i;
|
||||
END
|
||||
|
|
10
src/init.c
10
src/init.c
|
@ -16,7 +16,7 @@ void die(const char *msg)
|
|||
void die_extra(const char *msg, const char *extra)
|
||||
{
|
||||
kputs("\n");
|
||||
ksetcolor(COLOR_RED, COLOR_WHITE);
|
||||
ksetcolor(COLOR_LIGHTMAGENTA, COLOR_BLACK);
|
||||
kputs(msg);
|
||||
if((extra != nullptr) && (strlen(extra) > 0)) {
|
||||
kputs(": '");
|
||||
|
@ -102,14 +102,14 @@ static void dumpMB(const MultibootStructure *mbHeader)
|
|||
// TODO: MB_BOOTDEVICE
|
||||
if(mbHeader->flags & MB_COMMANDLINE)
|
||||
{
|
||||
kprintf("Commandline: %s\n", mbHeader->commandline);
|
||||
kprintf("Commandline: %s\n", (const char*)mbHeader->commandline);
|
||||
}
|
||||
if(mbHeader->flags & MB_MODULES)
|
||||
{
|
||||
const MultibootModule *mod = (const MultibootModule *)mbHeader->modules;
|
||||
for(size_t i = 0; i < mbHeader->moduleCount; i++)
|
||||
{
|
||||
kprintf("Module %s [%d - %d]\n", mod[i].name, mod[i].start, mod[i].end);
|
||||
kprintf("Module %s [%d - %d]\n", (const char*)mod[i].name, mod[i].start, mod[i].end);
|
||||
}
|
||||
}
|
||||
if(mbHeader->flags & MB_SYMS_AOUT)
|
||||
|
@ -136,7 +136,7 @@ static void dumpMB(const MultibootStructure *mbHeader)
|
|||
// TODO: MB_CONFIG_TABLE
|
||||
if(mbHeader->flags & MB_BOOTLOADER_NAME)
|
||||
{
|
||||
kprintf("Bootloader Name: %s\n", mbHeader->bootLoaderName);
|
||||
kprintf("Bootloader Name: %s\n", (const char*)mbHeader->bootLoaderName);
|
||||
}
|
||||
// TODO: MB_APS_TABLE
|
||||
}
|
||||
|
@ -204,7 +204,7 @@ void init(const MultibootStructure *mbHeader)
|
|||
cpp_init();
|
||||
putsuccess();
|
||||
|
||||
timer_add_callback(1, update_statusbar);
|
||||
timer_add_callback(1, update_statusbar);
|
||||
|
||||
vm_start();
|
||||
|
||||
|
|
|
@ -46,7 +46,7 @@ size_t allocatedMemory = 0;
|
|||
|
||||
static const size_t minimumAllocSize = 2 * sizeof(List);
|
||||
static char * const malloc_heap_start = (char *)0x400000;
|
||||
static char * const malloc_heap_end = (char *)0x10000000;
|
||||
static char * const malloc_heap_end = (char *)0x800000;
|
||||
|
||||
List *listBegin = nullptr;
|
||||
|
||||
|
@ -69,16 +69,18 @@ void malloc_print_list(int freeList)
|
|||
list,
|
||||
list->next,
|
||||
list->used ? list->allocationFile : "",
|
||||
list->used ? list->allocationLine : "",
|
||||
list->used ? list->allocationLine : 0,
|
||||
list->used,
|
||||
list->length);
|
||||
/*
|
||||
kprintf("[%x -> %x] (%s:%d) %d %d\n",
|
||||
list,
|
||||
list->next,
|
||||
list->used ? list->allocationFile : "",
|
||||
list->used ? list->allocationLine : "",
|
||||
list->used ? list->allocationLine : 0,
|
||||
list->used,
|
||||
list->length);
|
||||
*/
|
||||
if(list->used) {
|
||||
count++;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include <stdlib.h>
|
||||
#include <timer.h>
|
||||
#include <dynamic.h>
|
||||
#include <console.h>
|
||||
#include "../trainscript/tsvm.hpp"
|
||||
|
||||
using namespace ker;
|
||||
|
|
12
trainOS.pro
12
trainOS.pro
|
@ -43,7 +43,17 @@ HEADERS += \
|
|||
include/dynamic.h \
|
||||
include/config.h \
|
||||
include/serial.h \
|
||||
include/malloc.h
|
||||
include/malloc.h \
|
||||
trainscript/instruction.hpp \
|
||||
trainscript/method.hpp \
|
||||
trainscript/module.hpp \
|
||||
trainscript/scriptmethod.hpp \
|
||||
trainscript/type.hpp \
|
||||
trainscript/types.hpp \
|
||||
trainscript/variable.hpp \
|
||||
trainscript/executioncontext.hpp \
|
||||
trainscript/vm.hpp \
|
||||
trainscript/instructions.hpp
|
||||
|
||||
DISTFILES += \
|
||||
asm/intr_common_handler.S \
|
||||
|
|
|
@ -41,6 +41,7 @@ struct ParserData
|
|||
}
|
||||
}
|
||||
die_extra("ParserData::strdup", "out of strings");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
};
|
||||
|
|
34
trainscript/executioncontext.hpp
Normal file
34
trainscript/executioncontext.hpp
Normal file
|
@ -0,0 +1,34 @@
|
|||
#pragma once
|
||||
#include <inttypes.h>
|
||||
#include <ker/string.hpp>
|
||||
#include <ker/dictionary.hpp>
|
||||
|
||||
#include "variable.hpp"
|
||||
|
||||
namespace trainscript
|
||||
{
|
||||
class Module;
|
||||
|
||||
class ExecutionContext :
|
||||
public ker::Dictionary<ker::String, Variable*>
|
||||
{
|
||||
public:
|
||||
Module * const module = nullptr;
|
||||
|
||||
ExecutionContext(Module *mod) :
|
||||
ker::Dictionary<ker::String, Variable*>(),
|
||||
module(mod)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Variable *get(const ker::String &name)
|
||||
{
|
||||
if(this->contains(name)) {
|
||||
return this->at(name);
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
24
trainscript/instruction.hpp
Normal file
24
trainscript/instruction.hpp
Normal file
|
@ -0,0 +1,24 @@
|
|||
#pragma once
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "executioncontext.hpp"
|
||||
|
||||
namespace trainscript
|
||||
{
|
||||
class Instruction
|
||||
{
|
||||
public:
|
||||
virtual ~Instruction() { }
|
||||
|
||||
virtual Variable execute(ExecutionContext &context) const = 0;
|
||||
|
||||
virtual Type expectedResult(ExecutionContext &) const {
|
||||
return Type::Void;
|
||||
}
|
||||
|
||||
virtual bool validate(ExecutionContext &, ker::String &errorCode) const {
|
||||
errorCode = "";
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
404
trainscript/instructions.hpp
Normal file
404
trainscript/instructions.hpp
Normal file
|
@ -0,0 +1,404 @@
|
|||
#pragma once
|
||||
#include <stdlib.h>
|
||||
#include "module.hpp"
|
||||
|
||||
#include "method.hpp"
|
||||
#include "instruction.hpp"
|
||||
|
||||
namespace trainscript
|
||||
{
|
||||
class Block :
|
||||
public Instruction
|
||||
{
|
||||
public:
|
||||
ker::Vector<Instruction*> instructions;
|
||||
|
||||
~Block() {
|
||||
for(auto *instr : instructions) delete instr;
|
||||
}
|
||||
|
||||
bool validate(ExecutionContext &context, ker::String &errorCode) const {
|
||||
errorCode = "";
|
||||
for(auto *instr : instructions) {
|
||||
if(instr->validate(context, errorCode) == false)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Variable execute(ExecutionContext &context) const override {
|
||||
for(auto *instr : instructions) {
|
||||
instr->execute(context);
|
||||
}
|
||||
return Variable::Void;
|
||||
}
|
||||
};
|
||||
|
||||
class ConstantExpression :
|
||||
public Instruction
|
||||
{
|
||||
public:
|
||||
Variable value;
|
||||
ConstantExpression(Variable value) : value(value) { }
|
||||
|
||||
Variable execute(ExecutionContext &) const override {
|
||||
return this->value;
|
||||
}
|
||||
|
||||
Type expectedResult(ExecutionContext &) const override {
|
||||
return this->value.type;
|
||||
}
|
||||
};
|
||||
|
||||
class VariableExpression :
|
||||
public Instruction
|
||||
{
|
||||
public:
|
||||
ker::String variableName;
|
||||
VariableExpression(ker::String variableName) : variableName(variableName) { }
|
||||
|
||||
Variable execute(ExecutionContext &context) const override {
|
||||
auto *var = context.get(this->variableName);
|
||||
if(var == nullptr) {
|
||||
die_extra("VariableExpression.VariableNotFound", this->variableName.str());
|
||||
}
|
||||
return *var;
|
||||
}
|
||||
|
||||
bool validate(ExecutionContext &context, ker::String &errorCode) const override {
|
||||
errorCode = "";
|
||||
if(context.get(this->variableName) == nullptr) {
|
||||
errorCode = "Variable " + this->variableName + " not found.";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Type expectedResult(ExecutionContext &context) const override {
|
||||
Variable *var = context.get(this->variableName);
|
||||
if(var == nullptr) {
|
||||
return Type::Invalid;
|
||||
} else {
|
||||
return var->type;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class VariableAssignmentExpression :
|
||||
public Instruction
|
||||
{
|
||||
public:
|
||||
ker::String variableName;
|
||||
Instruction *expression;
|
||||
VariableAssignmentExpression(ker::String variableName, Instruction *expression) :
|
||||
variableName(variableName),
|
||||
expression(expression)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Variable execute(ExecutionContext &context) const override {
|
||||
if(this->expression == nullptr) {
|
||||
die("VariableAssignmentExpression.ExpressionMissing");
|
||||
}
|
||||
Variable result = this->expression->execute(context);
|
||||
|
||||
Variable *target = context.get(this->variableName);
|
||||
if(target == nullptr) {
|
||||
die_extra("VariableAssignmentExpression.VariableNotFound", this->variableName.str());
|
||||
}
|
||||
|
||||
if(target->type != result.type) {
|
||||
die_extra("VariableAssignmentExpression.ExpectedType", result.type.name());
|
||||
}
|
||||
|
||||
switch(target->type.id) {
|
||||
case TypeID::Int: target->integer = result.integer; break;
|
||||
case TypeID::Real: target->real = result.real; break;
|
||||
case TypeID::Bool: target->boolean = result.boolean; break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool validate(ExecutionContext &context, ker::String &errorCode) const override {
|
||||
errorCode = "";
|
||||
if(this->expression == nullptr) {
|
||||
errorCode = "Missing expression.";
|
||||
return false;
|
||||
}
|
||||
Type result = this->expression->expectedResult(context);
|
||||
if(result == Type::Invalid) {
|
||||
errorCode = "Expression returns invalid type.";
|
||||
return false;
|
||||
}
|
||||
if(result == Type::Void) {
|
||||
errorCode = "Void cannot be assigned to a variable";
|
||||
return false;
|
||||
}
|
||||
Variable *var = context.get(this->variableName);
|
||||
if(var == nullptr) {
|
||||
errorCode = "Variable " + this->variableName + " not found.";
|
||||
return false;
|
||||
}
|
||||
if(var->type != result) {
|
||||
errorCode = "Variable assignment has invalid type.";
|
||||
return false;
|
||||
}
|
||||
if(this->expression->validate(context, errorCode) == false)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
Type expectedResult(ExecutionContext &context) const override {
|
||||
if(this->expression == nullptr) {
|
||||
return Type::Invalid;
|
||||
} else {
|
||||
return this->expression->expectedResult(context);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class MethodInvokeExpression :
|
||||
public Instruction
|
||||
{
|
||||
public:
|
||||
ker::String methodName;
|
||||
ker::Vector<Instruction*> parameters;
|
||||
|
||||
MethodInvokeExpression(ker::String methodName) :
|
||||
methodName(methodName)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Variable execute(ExecutionContext &context) const override
|
||||
{
|
||||
Method *method = context.module->method(this->methodName.str());
|
||||
if(method == nullptr) {
|
||||
die_extra("MethodInvokeExpression.MethodNotFound", this->methodName.str());
|
||||
}
|
||||
|
||||
ker::Vector<Variable> vars(this->parameters.length());
|
||||
vars.resize(this->parameters.length());
|
||||
for(size_t i = 0; i < vars.length(); i++) {
|
||||
vars[i] = this->parameters.at(i)->execute(context);
|
||||
}
|
||||
|
||||
return method->invoke(vars);
|
||||
}
|
||||
|
||||
bool validate(ExecutionContext &context, ker::String &errorCode) const override {
|
||||
Method *method = context.module->method(this->methodName.str());
|
||||
if(method == nullptr) {
|
||||
errorCode = "The method " + this->methodName + " does not exist.";
|
||||
return false;
|
||||
}
|
||||
|
||||
ker::Vector<Type> arguments = method->arguments();
|
||||
if(arguments.length() != this->parameters.length()) {
|
||||
errorCode = "Argument count mismatch.";
|
||||
return false;
|
||||
}
|
||||
for(size_t i = 0; i < arguments.length(); i++) {
|
||||
if(this->parameters[i]->expectedResult(context) != arguments[i]) {
|
||||
errorCode = "Argument type mismatch.";
|
||||
return false;
|
||||
}
|
||||
if(this->parameters[i]->validate(context, errorCode) == false)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Type expectedResult(ExecutionContext &context) const override {
|
||||
Method *method = context.module->method(this->methodName.str());
|
||||
if(method == nullptr) {
|
||||
return Type::Invalid;
|
||||
}
|
||||
return method->returnType();
|
||||
}
|
||||
};
|
||||
|
||||
template<Variable (*OP)(Variable, Variable)>
|
||||
class ArithmeticExpression :
|
||||
public Instruction
|
||||
{
|
||||
public:
|
||||
Instruction *lhs, *rhs;
|
||||
|
||||
ArithmeticExpression(Instruction *lhs, Instruction *rhs) :
|
||||
lhs(lhs),
|
||||
rhs(rhs)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Variable execute(ExecutionContext &context) const override {
|
||||
if(this->lhs == nullptr) {
|
||||
die_extra("ArithmeticExpression.ExpressionMissing", "Left-hand side");
|
||||
}
|
||||
if(this->rhs == nullptr) {
|
||||
die_extra("ArithmeticExpression.ExpressionMissing", "Right-hand side");
|
||||
}
|
||||
|
||||
Variable left = this->lhs->execute(context);
|
||||
Variable right = this->rhs->execute(context);
|
||||
|
||||
if(left.type != right.type) {
|
||||
die("ArithmeticExpression.TypeMismatch");
|
||||
}
|
||||
|
||||
Variable result = OP(left, right);
|
||||
|
||||
if(result.type.usable() == false) {
|
||||
die_extra("ArithmeticExpression.InvalidResult", result.type.name());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool validate(ExecutionContext &context, ker::String &errorCode) const override {
|
||||
errorCode = "";
|
||||
if(this->lhs == nullptr) {
|
||||
errorCode = "Left part of operand is missing.";
|
||||
return false;
|
||||
}
|
||||
if(this->rhs == nullptr) {
|
||||
errorCode = "Right part of operand is missing.";
|
||||
return false;
|
||||
}
|
||||
Type lhsType = this->lhs->expectedResult(context);
|
||||
Type rhsType = this->rhs->expectedResult(context);
|
||||
if(lhsType != rhsType) {
|
||||
errorCode = "Types of operands do not match.";
|
||||
return false;
|
||||
}
|
||||
if(lhsType == Type::Invalid) {
|
||||
errorCode = "Invalid type can't be used for operand.";
|
||||
return false;
|
||||
}
|
||||
if(rhsType == Type::Void) {
|
||||
errorCode = "VOID type can't be used for operand.";
|
||||
return false;
|
||||
}
|
||||
|
||||
if(this->lhs->validate(context, errorCode) == false)
|
||||
return false;
|
||||
if(this->rhs->validate(context, errorCode) == false)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Type expectedResult(ExecutionContext &context) const override {
|
||||
if(this->lhs == nullptr) {
|
||||
return Type::Invalid;
|
||||
} else {
|
||||
return this->lhs->expectedResult(context);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class IfExpression :
|
||||
public Instruction
|
||||
{
|
||||
public:
|
||||
Instruction *condition;
|
||||
Instruction *blockTrue;
|
||||
Instruction *blockFalse;
|
||||
|
||||
IfExpression(Instruction *condition, Instruction *blockTrue, Instruction *blockFalse) :
|
||||
condition(condition),
|
||||
blockTrue(blockTrue),
|
||||
blockFalse(blockFalse)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Variable execute(ExecutionContext &context) const override {
|
||||
if(this->condition == nullptr) {
|
||||
die("IfExpression.ConditionMissing");
|
||||
}
|
||||
|
||||
Variable result = this->condition->execute(context);
|
||||
if(result.type != Type::Boolean) {
|
||||
die_extra("IfExpression.TypeMismatch", result.type.name());
|
||||
}
|
||||
if((result.boolean == true) && (this->blockTrue != nullptr)) {
|
||||
this->blockTrue->execute(context);
|
||||
}
|
||||
if((result.boolean == false) && (this->blockFalse != nullptr)) {
|
||||
this->blockFalse->execute(context);
|
||||
}
|
||||
return Variable::Void;
|
||||
}
|
||||
};
|
||||
|
||||
class RepeatEndlessExpression :
|
||||
public Instruction
|
||||
{
|
||||
public:
|
||||
Instruction *block;
|
||||
|
||||
RepeatEndlessExpression(Instruction *block) :
|
||||
block(block)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Variable execute(ExecutionContext &context) const override {
|
||||
if(this->block == nullptr) {
|
||||
die("RepeatEndlessExpression.BlockMissing");
|
||||
}
|
||||
|
||||
while(true)
|
||||
{
|
||||
Variable result = this->block->execute(context);
|
||||
(void)result;
|
||||
}
|
||||
return Variable::Void;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class RepeatWhileExpression :
|
||||
public Instruction
|
||||
{
|
||||
public:
|
||||
Instruction *condition;
|
||||
Instruction *block;
|
||||
|
||||
RepeatWhileExpression(Instruction *condition, Instruction *block) :
|
||||
condition(condition),
|
||||
block(block)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Variable execute(ExecutionContext &context) const override {
|
||||
if(this->condition == nullptr) {
|
||||
die("RepeatWhileExpression.ConditionMissing");
|
||||
}
|
||||
if(this->block == nullptr) {
|
||||
die("RepeatWhileExpression.BlockMissing");
|
||||
}
|
||||
|
||||
while(true)
|
||||
{
|
||||
Variable cond = this->condition->execute(context);
|
||||
if(cond.type != Type::Boolean) {
|
||||
return Variable::Invalid;
|
||||
}
|
||||
if(cond.boolean == false) {
|
||||
break;
|
||||
}
|
||||
|
||||
this->block->execute(context);
|
||||
}
|
||||
return Variable::Void;
|
||||
}
|
||||
};
|
||||
}
|
22
trainscript/method.hpp
Normal file
22
trainscript/method.hpp
Normal file
|
@ -0,0 +1,22 @@
|
|||
#pragma once
|
||||
#include <inttypes.h>
|
||||
#include <ker/string.hpp>
|
||||
|
||||
#include "type.hpp"
|
||||
|
||||
namespace trainscript
|
||||
{
|
||||
class Method
|
||||
{
|
||||
public:
|
||||
virtual ~Method() { }
|
||||
|
||||
virtual Variable invoke(ker::Vector<Variable> arguments) = 0;
|
||||
|
||||
virtual bool validate(ker::String &errorCode) const = 0;
|
||||
|
||||
virtual ker::Vector<Type> arguments() const = 0;
|
||||
|
||||
virtual Type returnType() const = 0;
|
||||
};
|
||||
}
|
32
trainscript/module.hpp
Normal file
32
trainscript/module.hpp
Normal file
|
@ -0,0 +1,32 @@
|
|||
#pragma once
|
||||
#include <inttypes.h>
|
||||
#include <ker/string.hpp>
|
||||
#include <ker/dictionary.hpp>
|
||||
|
||||
#include "variable.hpp"
|
||||
#include "method.hpp"
|
||||
|
||||
namespace trainscript
|
||||
{
|
||||
class Module
|
||||
{
|
||||
public:
|
||||
ker::Dictionary<ker::String, Variable*> variables;
|
||||
ker::Dictionary<ker::String, Method*> methods;
|
||||
public:
|
||||
Module();
|
||||
~Module();
|
||||
|
||||
Method *method(const char *name)
|
||||
{
|
||||
return this->methods.get(name);
|
||||
}
|
||||
|
||||
Variable *variable(const char *name)
|
||||
{
|
||||
return this->variables.get(name);
|
||||
}
|
||||
|
||||
bool validate(ker::String &errorCode) const;
|
||||
};
|
||||
}
|
42
trainscript/scriptmethod.hpp
Normal file
42
trainscript/scriptmethod.hpp
Normal file
|
@ -0,0 +1,42 @@
|
|||
#pragma once
|
||||
|
||||
#include "module.hpp"
|
||||
#include "method.hpp"
|
||||
|
||||
namespace trainscript
|
||||
{
|
||||
class ScriptMethod :
|
||||
public Method
|
||||
{
|
||||
public:
|
||||
Module *module;
|
||||
Instruction *block;
|
||||
bool isPublic;
|
||||
ker::Vector<ker::Pair<ker::String, Type>> mArguments;
|
||||
ker::Dictionary<ker::String, Type> mLocals;
|
||||
ker::Pair<ker::String, Type> mReturnValue;
|
||||
|
||||
ScriptMethod(Module *module, Instruction *block) : module(module), block(block)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Variable invoke(ker::Vector<Variable> arguments) override;
|
||||
|
||||
bool validate(ker::String &errorCode) const override;
|
||||
|
||||
ker::Vector<Type> arguments() const override
|
||||
{
|
||||
ker::Vector<Type> args(this->mArguments.length());
|
||||
for(size_t i = 0; i < args.length(); i++) {
|
||||
args[i] = this->mArguments[i].second;
|
||||
}
|
||||
return args;
|
||||
}
|
||||
|
||||
Type returnType() const override
|
||||
{
|
||||
return this->mReturnValue.second;
|
||||
}
|
||||
};
|
||||
}
|
|
@ -1,128 +0,0 @@
|
|||
/* A Bison parser, made by GNU Bison 3.0.4. */
|
||||
|
||||
/* Bison interface for Yacc-like parsers in C
|
||||
|
||||
Copyright (C) 1984, 1989-1990, 2000-2015 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
/* As a special exception, you may create a larger work that contains
|
||||
part or all of the Bison parser skeleton and distribute that work
|
||||
under terms of your choice, so long as that work isn't itself a
|
||||
parser generator using the skeleton or a modified version thereof
|
||||
as a parser skeleton. Alternatively, if you modify or redistribute
|
||||
the parser skeleton itself, you may (at your option) remove this
|
||||
special exception, which will cause the skeleton and the resulting
|
||||
Bison output files to be licensed under the GNU General Public
|
||||
License without this special exception.
|
||||
|
||||
This special exception was added by the Free Software Foundation in
|
||||
version 2.2 of Bison. */
|
||||
|
||||
#ifndef YY_YY_TRAINSCRIPT_TRAINSCRIPT_TAB_HPP_INCLUDED
|
||||
# define YY_YY_TRAINSCRIPT_TRAINSCRIPT_TAB_HPP_INCLUDED
|
||||
/* Debug traces. */
|
||||
#ifndef YYDEBUG
|
||||
# define YYDEBUG 0
|
||||
#endif
|
||||
#if YYDEBUG
|
||||
extern int yydebug;
|
||||
#endif
|
||||
|
||||
/* Token type. */
|
||||
#ifndef YYTOKENTYPE
|
||||
# define YYTOKENTYPE
|
||||
enum yytokentype
|
||||
{
|
||||
TAB = 258,
|
||||
TYPENAME = 259,
|
||||
SEMICOLON = 260,
|
||||
COLON = 261,
|
||||
COMMA = 262,
|
||||
PIPE = 263,
|
||||
PLUS = 264,
|
||||
MINUS = 265,
|
||||
MULTIPLY = 266,
|
||||
DIVIDE = 267,
|
||||
MODULO = 268,
|
||||
LBRACKET = 269,
|
||||
RBRACKET = 270,
|
||||
RARROW = 271,
|
||||
LARROW = 272,
|
||||
OP_LT = 273,
|
||||
OP_LE = 274,
|
||||
OP_GT = 275,
|
||||
OP_GE = 276,
|
||||
OP_EQ = 277,
|
||||
OP_NEQ = 278,
|
||||
REAL = 279,
|
||||
INT = 280,
|
||||
IDENTIFIER = 281,
|
||||
KW_PUB = 282,
|
||||
KW_PRI = 283,
|
||||
KW_VAR = 284,
|
||||
KW_PTR = 285,
|
||||
KW_VOID = 286,
|
||||
KW_INT = 287,
|
||||
KW_REAL = 288,
|
||||
KW_TEXT = 289,
|
||||
KW_BOOL = 290,
|
||||
KW_BEGIN = 291,
|
||||
KW_END = 292,
|
||||
KW_IF = 293,
|
||||
KW_THEN = 294,
|
||||
KW_ELSE = 295,
|
||||
KW_ELSEIF = 296,
|
||||
KW_REPEAT = 297,
|
||||
KW_FROM = 298,
|
||||
KW_TO = 299,
|
||||
KW_UNTIL = 300,
|
||||
KW_WHILE = 301,
|
||||
KW_DO = 302
|
||||
};
|
||||
#endif
|
||||
|
||||
/* Value type. */
|
||||
#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
|
||||
|
||||
union YYSTYPE
|
||||
{
|
||||
#line 44 "trainscript/trainscript.y" /* yacc.c:1909 */
|
||||
|
||||
float fval;
|
||||
int ival;
|
||||
char *text;
|
||||
int indentation;
|
||||
trainscript::Type type;
|
||||
VariableDeclaration varDecl;
|
||||
MethodDeclaration method;
|
||||
MethodBody *body;
|
||||
MethodHeader methodHeader;
|
||||
trainscript::Instruction *instruction;
|
||||
LocalVariable *local;
|
||||
ExpressionList *expressions;
|
||||
|
||||
#line 117 "trainscript/trainscript.tab.hpp" /* yacc.c:1909 */
|
||||
};
|
||||
|
||||
typedef union YYSTYPE YYSTYPE;
|
||||
# define YYSTYPE_IS_TRIVIAL 1
|
||||
# define YYSTYPE_IS_DECLARED 1
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
int yyparse (ParserData * context);
|
||||
|
||||
#endif /* !YY_YY_TRAINSCRIPT_TRAINSCRIPT_TAB_HPP_INCLUDED */
|
|
@ -1,6 +1,7 @@
|
|||
%{
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <console.h>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
|
|
|
@ -10,8 +10,6 @@
|
|||
|
||||
namespace trainscript
|
||||
{
|
||||
bool verbose = false;
|
||||
|
||||
const Type Type::Invalid = { TypeID::Invalid, 0 };
|
||||
const Type Type::Void = { TypeID::Void, 0 };
|
||||
const Type Type::Int = { TypeID::Int, 0 };
|
||||
|
@ -94,7 +92,7 @@ namespace trainscript
|
|||
|
||||
Variable ScriptMethod::invoke(ker::Vector<Variable> arguments)
|
||||
{
|
||||
LocalContext context(this->module);
|
||||
ExecutionContext context(this->module);
|
||||
|
||||
ker::Vector<Variable*> temporaries;
|
||||
|
||||
|
@ -143,7 +141,7 @@ namespace trainscript
|
|||
return false;
|
||||
}
|
||||
|
||||
LocalContext context(this->module);
|
||||
ExecutionContext context(this->module);
|
||||
|
||||
for(auto var : this->module->variables)
|
||||
{
|
||||
|
|
|
@ -1,644 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
extern "C" {
|
||||
#include <stdlib.h>
|
||||
#include <console.h>
|
||||
}
|
||||
|
||||
#include <ker/string.hpp>
|
||||
#include <ker/vector.hpp>
|
||||
#include <ker/dictionary.hpp>
|
||||
|
||||
#include "typeid.hpp"
|
||||
|
||||
namespace trainscript
|
||||
{
|
||||
using Int = int32_t;
|
||||
using Real = float;
|
||||
using Void = void;
|
||||
using Bool = bool;
|
||||
|
||||
extern bool verbose;
|
||||
|
||||
struct Text
|
||||
{
|
||||
size_t length;
|
||||
char *data;
|
||||
};
|
||||
|
||||
struct Type
|
||||
{
|
||||
TypeID id;
|
||||
int pointer;
|
||||
|
||||
Type reference() const {
|
||||
return { id, pointer + 1 };
|
||||
}
|
||||
|
||||
Type dereference() const {
|
||||
return { id, pointer - 1 };
|
||||
}
|
||||
|
||||
bool usable() const {
|
||||
return (this->id != TypeID::Invalid) &&
|
||||
((this->id != TypeID::Void) || (this->pointer > 0));
|
||||
}
|
||||
|
||||
bool operator ==(const Type &other) const {
|
||||
return (this->id == other.id) &&
|
||||
(this->pointer == other.pointer);
|
||||
}
|
||||
|
||||
bool operator !=(const Type &other) const {
|
||||
return (this->id != other.id) ||
|
||||
(this->pointer != other.pointer);
|
||||
}
|
||||
|
||||
const char *name() const {
|
||||
switch(id) {
|
||||
case TypeID::Invalid: return "INVALID";
|
||||
case TypeID::Void: return "VOID";
|
||||
case TypeID::Int: return "INT";
|
||||
case TypeID::Real: return "REAL";
|
||||
case TypeID::Text: return "TEXT";
|
||||
case TypeID::Bool: return "BOOL";
|
||||
default: return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
static const Type Invalid;
|
||||
static const Type Void;
|
||||
static const Type Int;
|
||||
static const Type Real;
|
||||
static const Type Text;
|
||||
static const Type Boolean;
|
||||
};
|
||||
|
||||
struct Variable
|
||||
{
|
||||
Type type;
|
||||
union {
|
||||
Int integer;
|
||||
Real real;
|
||||
Text text;
|
||||
Bool boolean;
|
||||
};
|
||||
|
||||
void printval() const
|
||||
{
|
||||
switch(this->type.id) {
|
||||
case TypeID::Int: kprintf("%d", this->integer); break;
|
||||
case TypeID::Real: kprintf("%f", this->real); break;
|
||||
case TypeID::Bool: kprintf("%s", this->boolean ? "TRUE" : "FALSE"); break;
|
||||
default: kprintf("???"); break;
|
||||
}
|
||||
}
|
||||
|
||||
static const Variable Invalid;
|
||||
static const Variable Void;
|
||||
static const Variable Int;
|
||||
static const Variable Real;
|
||||
static const Variable Text;
|
||||
static const Variable Boolean;
|
||||
};
|
||||
|
||||
static inline Variable mkvar(Int value) {
|
||||
Variable v = Variable::Int;
|
||||
v.integer = value;
|
||||
return v;
|
||||
}
|
||||
|
||||
static inline Variable mkvar(Real value) {
|
||||
Variable v = Variable::Real;
|
||||
v.real = value;
|
||||
return v;
|
||||
}
|
||||
|
||||
static inline Variable mkbool(Bool value) {
|
||||
Variable v = Variable::Boolean;
|
||||
v.boolean = value;
|
||||
return v;
|
||||
}
|
||||
|
||||
class Module;
|
||||
|
||||
class LocalContext :
|
||||
public ker::Dictionary<ker::String, Variable*>
|
||||
{
|
||||
public:
|
||||
Module * const module = nullptr;
|
||||
|
||||
LocalContext(Module *mod) :
|
||||
ker::Dictionary<ker::String, Variable*>(),
|
||||
module(mod)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Variable *get(const ker::String &name)
|
||||
{
|
||||
if(this->contains(name)) {
|
||||
return this->at(name);
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class Instruction
|
||||
{
|
||||
public:
|
||||
virtual ~Instruction() { }
|
||||
|
||||
virtual Variable execute(LocalContext &context) const = 0;
|
||||
|
||||
virtual Type expectedResult(LocalContext &) const {
|
||||
return Type::Void;
|
||||
}
|
||||
|
||||
virtual bool validate(LocalContext &, ker::String &errorCode) const {
|
||||
errorCode = "";
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class Block :
|
||||
public Instruction
|
||||
{
|
||||
public:
|
||||
ker::Vector<Instruction*> instructions;
|
||||
|
||||
~Block() {
|
||||
for(auto *instr : instructions) delete instr;
|
||||
}
|
||||
|
||||
bool validate(LocalContext &context, ker::String &errorCode) const {
|
||||
errorCode = "";
|
||||
for(auto *instr : instructions) {
|
||||
if(instr->validate(context, errorCode) == false)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Variable execute(LocalContext &context) const override {
|
||||
for(auto *instr : instructions) {
|
||||
instr->execute(context);
|
||||
}
|
||||
return Variable::Void;
|
||||
}
|
||||
};
|
||||
|
||||
class Method
|
||||
{
|
||||
public:
|
||||
virtual ~Method() { }
|
||||
|
||||
virtual Variable invoke(ker::Vector<Variable> arguments) = 0;
|
||||
|
||||
virtual bool validate(ker::String &errorCode) const = 0;
|
||||
|
||||
virtual ker::Vector<Type> arguments() const = 0;
|
||||
|
||||
virtual Type returnType() const = 0;
|
||||
};
|
||||
|
||||
class ScriptMethod :
|
||||
public Method
|
||||
{
|
||||
public:
|
||||
Module *module;
|
||||
Instruction *block;
|
||||
bool isPublic;
|
||||
ker::Vector<ker::Pair<ker::String, Type>> mArguments;
|
||||
ker::Dictionary<ker::String, Type> mLocals;
|
||||
ker::Pair<ker::String, Type> mReturnValue;
|
||||
|
||||
ScriptMethod(Module *module, Instruction *block) : module(module), block(block)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Variable invoke(ker::Vector<Variable> arguments) override;
|
||||
|
||||
bool validate(ker::String &errorCode) const override;
|
||||
|
||||
ker::Vector<Type> arguments() const override
|
||||
{
|
||||
ker::Vector<Type> args(this->mArguments.length());
|
||||
for(size_t i = 0; i < args.length(); i++) {
|
||||
args[i] = this->mArguments[i].second;
|
||||
}
|
||||
return args;
|
||||
}
|
||||
|
||||
Type returnType() const override
|
||||
{
|
||||
return this->mReturnValue.second;
|
||||
}
|
||||
};
|
||||
|
||||
class Module
|
||||
{
|
||||
public:
|
||||
ker::Dictionary<ker::String, Variable*> variables;
|
||||
ker::Dictionary<ker::String, Method*> methods;
|
||||
public:
|
||||
Module();
|
||||
~Module();
|
||||
|
||||
Method *method(const char *name)
|
||||
{
|
||||
return this->methods.get(name);
|
||||
}
|
||||
|
||||
Variable *variable(const char *name)
|
||||
{
|
||||
return this->variables.get(name);
|
||||
}
|
||||
|
||||
bool validate(ker::String &errorCode) const;
|
||||
};
|
||||
|
||||
class VM
|
||||
{
|
||||
public:
|
||||
static Module *load(const void *buffer, size_t length);
|
||||
|
||||
static Module *load(const char *text);
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Instructions
|
||||
|
||||
class ConstantExpression :
|
||||
public Instruction
|
||||
{
|
||||
public:
|
||||
Variable value;
|
||||
ConstantExpression(Variable value) : value(value) { }
|
||||
|
||||
Variable execute(LocalContext &) const override {
|
||||
return this->value;
|
||||
}
|
||||
|
||||
Type expectedResult(LocalContext &) const override {
|
||||
return this->value.type;
|
||||
}
|
||||
};
|
||||
|
||||
class VariableExpression :
|
||||
public Instruction
|
||||
{
|
||||
public:
|
||||
ker::String variableName;
|
||||
VariableExpression(ker::String variableName) : variableName(variableName) { }
|
||||
|
||||
Variable execute(LocalContext &context) const override {
|
||||
auto *var = context.get(this->variableName);
|
||||
if(var == nullptr) {
|
||||
die_extra("VariableExpression.VariableNotFound", this->variableName.str());
|
||||
}
|
||||
return *var;
|
||||
}
|
||||
|
||||
bool validate(LocalContext &context, ker::String &errorCode) const override {
|
||||
errorCode = "";
|
||||
if(context.get(this->variableName) == nullptr) {
|
||||
errorCode = "Variable " + this->variableName + " not found.";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Type expectedResult(LocalContext &context) const override {
|
||||
Variable *var = context.get(this->variableName);
|
||||
if(var == nullptr) {
|
||||
return Type::Invalid;
|
||||
} else {
|
||||
return var->type;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class VariableAssignmentExpression :
|
||||
public Instruction
|
||||
{
|
||||
public:
|
||||
ker::String variableName;
|
||||
Instruction *expression;
|
||||
VariableAssignmentExpression(ker::String variableName, Instruction *expression) :
|
||||
variableName(variableName),
|
||||
expression(expression)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Variable execute(LocalContext &context) const override {
|
||||
if(this->expression == nullptr) {
|
||||
die("VariableAssignmentExpression.ExpressionMissing");
|
||||
}
|
||||
Variable result = this->expression->execute(context);
|
||||
|
||||
Variable *target = context.get(this->variableName);
|
||||
if(target == nullptr) {
|
||||
die_extra("VariableAssignmentExpression.VariableNotFound", this->variableName.str());
|
||||
}
|
||||
|
||||
if(target->type != result.type) {
|
||||
die_extra("VariableAssignmentExpression.ExpectedType", result.type.name());
|
||||
}
|
||||
|
||||
switch(target->type.id) {
|
||||
case TypeID::Int: target->integer = result.integer; break;
|
||||
case TypeID::Real: target->real = result.real; break;
|
||||
case TypeID::Bool: target->boolean = result.boolean; break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool validate(LocalContext &context, ker::String &errorCode) const override {
|
||||
errorCode = "";
|
||||
if(this->expression == nullptr) {
|
||||
errorCode = "Missing expression.";
|
||||
return false;
|
||||
}
|
||||
Type result = this->expression->expectedResult(context);
|
||||
if(result == Type::Invalid) {
|
||||
errorCode = "Expression returns invalid type.";
|
||||
return false;
|
||||
}
|
||||
if(result == Type::Void) {
|
||||
errorCode = "Void cannot be assigned to a variable";
|
||||
return false;
|
||||
}
|
||||
Variable *var = context.get(this->variableName);
|
||||
if(var == nullptr) {
|
||||
errorCode = "Variable " + this->variableName + " not found.";
|
||||
return false;
|
||||
}
|
||||
if(var->type != result) {
|
||||
errorCode = "Variable assignment has invalid type.";
|
||||
return false;
|
||||
}
|
||||
if(this->expression->validate(context, errorCode) == false)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
Type expectedResult(LocalContext &context) const override {
|
||||
if(this->expression == nullptr) {
|
||||
return Type::Invalid;
|
||||
} else {
|
||||
return this->expression->expectedResult(context);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class MethodInvokeExpression :
|
||||
public Instruction
|
||||
{
|
||||
public:
|
||||
ker::String methodName;
|
||||
ker::Vector<Instruction*> parameters;
|
||||
|
||||
MethodInvokeExpression(ker::String methodName) :
|
||||
methodName(methodName)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Variable execute(LocalContext &context) const override
|
||||
{
|
||||
Method *method = context.module->method(this->methodName.str());
|
||||
if(method == nullptr) {
|
||||
die_extra("MethodInvokeExpression.MethodNotFound", this->methodName.str());
|
||||
}
|
||||
|
||||
ker::Vector<Variable> vars(this->parameters.length());
|
||||
vars.resize(this->parameters.length());
|
||||
for(size_t i = 0; i < vars.length(); i++) {
|
||||
vars[i] = this->parameters.at(i)->execute(context);
|
||||
}
|
||||
|
||||
return method->invoke(vars);
|
||||
}
|
||||
|
||||
bool validate(LocalContext &context, ker::String &errorCode) const override {
|
||||
Method *method = context.module->method(this->methodName.str());
|
||||
if(method == nullptr) {
|
||||
errorCode = "The method " + this->methodName + " does not exist.";
|
||||
return false;
|
||||
}
|
||||
|
||||
ker::Vector<Type> arguments = method->arguments();
|
||||
if(arguments.length() != this->parameters.length()) {
|
||||
errorCode = "Argument count mismatch.";
|
||||
return false;
|
||||
}
|
||||
for(size_t i = 0; i < arguments.length(); i++) {
|
||||
if(this->parameters[i]->expectedResult(context) != arguments[i]) {
|
||||
errorCode = "Argument type mismatch.";
|
||||
return false;
|
||||
}
|
||||
if(this->parameters[i]->validate(context, errorCode) == false)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Type expectedResult(LocalContext &context) const override {
|
||||
Method *method = context.module->method(this->methodName.str());
|
||||
if(method == nullptr) {
|
||||
return Type::Invalid;
|
||||
}
|
||||
return method->returnType();
|
||||
}
|
||||
};
|
||||
|
||||
template<Variable (*OP)(Variable, Variable)>
|
||||
class ArithmeticExpression :
|
||||
public Instruction
|
||||
{
|
||||
public:
|
||||
Instruction *lhs, *rhs;
|
||||
|
||||
ArithmeticExpression(Instruction *lhs, Instruction *rhs) :
|
||||
lhs(lhs),
|
||||
rhs(rhs)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Variable execute(LocalContext &context) const override {
|
||||
if(this->lhs == nullptr) {
|
||||
die_extra("ArithmeticExpression.ExpressionMissing", "Left-hand side");
|
||||
}
|
||||
if(this->rhs == nullptr) {
|
||||
die_extra("ArithmeticExpression.ExpressionMissing", "Right-hand side");
|
||||
}
|
||||
|
||||
Variable left = this->lhs->execute(context);
|
||||
Variable right = this->rhs->execute(context);
|
||||
|
||||
if(left.type != right.type) {
|
||||
die("ArithmeticExpression.TypeMismatch");
|
||||
}
|
||||
|
||||
Variable result = OP(left, right);
|
||||
|
||||
if(result.type.usable() == false) {
|
||||
die_extra("ArithmeticExpression.InvalidResult", result.type.name());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool validate(LocalContext &context, ker::String &errorCode) const override {
|
||||
errorCode = "";
|
||||
if(this->lhs == nullptr) {
|
||||
errorCode = "Left part of operand is missing.";
|
||||
return false;
|
||||
}
|
||||
if(this->rhs == nullptr) {
|
||||
errorCode = "Right part of operand is missing.";
|
||||
return false;
|
||||
}
|
||||
Type lhsType = this->lhs->expectedResult(context);
|
||||
Type rhsType = this->rhs->expectedResult(context);
|
||||
if(lhsType != rhsType) {
|
||||
errorCode = "Types of operands do not match.";
|
||||
return false;
|
||||
}
|
||||
if(lhsType == Type::Invalid) {
|
||||
errorCode = "Invalid type can't be used for operand.";
|
||||
return false;
|
||||
}
|
||||
if(rhsType == Type::Void) {
|
||||
errorCode = "VOID type can't be used for operand.";
|
||||
return false;
|
||||
}
|
||||
|
||||
if(this->lhs->validate(context, errorCode) == false)
|
||||
return false;
|
||||
if(this->rhs->validate(context, errorCode) == false)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Type expectedResult(LocalContext &context) const override {
|
||||
if(this->lhs == nullptr) {
|
||||
return Type::Invalid;
|
||||
} else {
|
||||
return this->lhs->expectedResult(context);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class IfExpression :
|
||||
public Instruction
|
||||
{
|
||||
public:
|
||||
Instruction *condition;
|
||||
Instruction *blockTrue;
|
||||
Instruction *blockFalse;
|
||||
|
||||
IfExpression(Instruction *condition, Instruction *blockTrue, Instruction *blockFalse) :
|
||||
condition(condition),
|
||||
blockTrue(blockTrue),
|
||||
blockFalse(blockFalse)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Variable execute(LocalContext &context) const override {
|
||||
if(this->condition == nullptr) {
|
||||
die("IfExpression.ConditionMissing");
|
||||
}
|
||||
|
||||
Variable result = this->condition->execute(context);
|
||||
if(result.type != Type::Boolean) {
|
||||
die_extra("IfExpression.TypeMismatch", result.type.name());
|
||||
}
|
||||
if((result.boolean == true) && (this->blockTrue != nullptr)) {
|
||||
this->blockTrue->execute(context);
|
||||
}
|
||||
if((result.boolean == false) && (this->blockFalse != nullptr)) {
|
||||
this->blockFalse->execute(context);
|
||||
}
|
||||
return Variable::Void;
|
||||
}
|
||||
};
|
||||
|
||||
class RepeatEndlessExpression :
|
||||
public Instruction
|
||||
{
|
||||
public:
|
||||
Instruction *block;
|
||||
|
||||
RepeatEndlessExpression(Instruction *block) :
|
||||
block(block)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Variable execute(LocalContext &context) const override {
|
||||
if(this->block == nullptr) {
|
||||
die("RepeatEndlessExpression.BlockMissing");
|
||||
}
|
||||
|
||||
while(true)
|
||||
{
|
||||
Variable result = this->block->execute(context);
|
||||
(void)result;
|
||||
}
|
||||
return Variable::Void;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class RepeatWhileExpression :
|
||||
public Instruction
|
||||
{
|
||||
public:
|
||||
Instruction *condition;
|
||||
Instruction *block;
|
||||
|
||||
RepeatWhileExpression(Instruction *condition, Instruction *block) :
|
||||
condition(condition),
|
||||
block(block)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Variable execute(LocalContext &context) const override {
|
||||
if(this->condition == nullptr) {
|
||||
die("RepeatWhileExpression.ConditionMissing");
|
||||
}
|
||||
if(this->block == nullptr) {
|
||||
die("RepeatWhileExpression.BlockMissing");
|
||||
}
|
||||
|
||||
while(true)
|
||||
{
|
||||
Variable cond = this->condition->execute(context);
|
||||
if(cond.type != Type::Boolean) {
|
||||
return Variable::Invalid;
|
||||
}
|
||||
if(cond.boolean == false) {
|
||||
break;
|
||||
}
|
||||
|
||||
this->block->execute(context);
|
||||
}
|
||||
return Variable::Void;
|
||||
}
|
||||
};
|
||||
}
|
||||
#include "vm.hpp"
|
||||
#include "method.hpp"
|
||||
#include "instructions.hpp"
|
||||
#include "scriptmethod.hpp"
|
||||
|
|
56
trainscript/type.hpp
Normal file
56
trainscript/type.hpp
Normal file
|
@ -0,0 +1,56 @@
|
|||
#pragma once
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "types.hpp"
|
||||
#include "typeid.hpp"
|
||||
|
||||
namespace trainscript
|
||||
{
|
||||
struct Type
|
||||
{
|
||||
TypeID id;
|
||||
int pointer;
|
||||
|
||||
Type reference() const {
|
||||
return { id, pointer + 1 };
|
||||
}
|
||||
|
||||
Type dereference() const {
|
||||
return { id, pointer - 1 };
|
||||
}
|
||||
|
||||
bool usable() const {
|
||||
return (this->id != TypeID::Invalid) &&
|
||||
((this->id != TypeID::Void) || (this->pointer > 0));
|
||||
}
|
||||
|
||||
bool operator ==(const Type &other) const {
|
||||
return (this->id == other.id) &&
|
||||
(this->pointer == other.pointer);
|
||||
}
|
||||
|
||||
bool operator !=(const Type &other) const {
|
||||
return (this->id != other.id) ||
|
||||
(this->pointer != other.pointer);
|
||||
}
|
||||
|
||||
const char *name() const {
|
||||
switch(id) {
|
||||
case TypeID::Invalid: return "INVALID";
|
||||
case TypeID::Void: return "VOID";
|
||||
case TypeID::Int: return "INT";
|
||||
case TypeID::Real: return "REAL";
|
||||
case TypeID::Text: return "TEXT";
|
||||
case TypeID::Bool: return "BOOL";
|
||||
default: return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
static const Type Invalid;
|
||||
static const Type Void;
|
||||
static const Type Int;
|
||||
static const Type Real;
|
||||
static const Type Text;
|
||||
static const Type Boolean;
|
||||
};
|
||||
}
|
13
trainscript/types.hpp
Normal file
13
trainscript/types.hpp
Normal file
|
@ -0,0 +1,13 @@
|
|||
#pragma once
|
||||
#include <inttypes.h>
|
||||
|
||||
#include <ker/string.hpp>
|
||||
|
||||
namespace trainscript
|
||||
{
|
||||
using Int = int32_t;
|
||||
using Real = float;
|
||||
using Void = void;
|
||||
using Bool = bool;
|
||||
using Text = ker::String;
|
||||
}
|
55
trainscript/variable.hpp
Normal file
55
trainscript/variable.hpp
Normal file
|
@ -0,0 +1,55 @@
|
|||
#pragma once
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "type.hpp"
|
||||
|
||||
namespace trainscript
|
||||
{
|
||||
struct Variable
|
||||
{
|
||||
Type type;
|
||||
union {
|
||||
Int integer;
|
||||
Real real;
|
||||
// Text text;
|
||||
Bool boolean;
|
||||
};
|
||||
|
||||
#if defined(TSVM_PRINTVAL)
|
||||
void printval() const
|
||||
{
|
||||
switch(this->type.id) {
|
||||
case TypeID::Int: kprintf("%d", this->integer); break;
|
||||
case TypeID::Real: kprintf("%f", this->real); break;
|
||||
case TypeID::Bool: kprintf("%s", this->boolean ? "TRUE" : "FALSE"); break;
|
||||
default: kprintf("???"); break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static const Variable Invalid;
|
||||
static const Variable Void;
|
||||
static const Variable Int;
|
||||
static const Variable Real;
|
||||
static const Variable Text;
|
||||
static const Variable Boolean;
|
||||
};
|
||||
|
||||
static inline Variable mkvar(Int value) {
|
||||
Variable v = Variable::Int;
|
||||
v.integer = value;
|
||||
return v;
|
||||
}
|
||||
|
||||
static inline Variable mkvar(Real value) {
|
||||
Variable v = Variable::Real;
|
||||
v.real = value;
|
||||
return v;
|
||||
}
|
||||
|
||||
static inline Variable mkbool(Bool value) {
|
||||
Variable v = Variable::Boolean;
|
||||
v.boolean = value;
|
||||
return v;
|
||||
}
|
||||
}
|
14
trainscript/vm.hpp
Normal file
14
trainscript/vm.hpp
Normal file
|
@ -0,0 +1,14 @@
|
|||
#pragma once
|
||||
#include <stdlib.h>
|
||||
#include "module.hpp"
|
||||
|
||||
namespace trainscript
|
||||
{
|
||||
class VM
|
||||
{
|
||||
public:
|
||||
static Module *load(const void *buffer, size_t length);
|
||||
|
||||
static Module *load(const char *text);
|
||||
};
|
||||
}
|
Loading…
Reference in a new issue