Refactors tsvm.hpp into more subfiles for better structure.

This commit is contained in:
Felix Queissner 2015-08-19 16:26:28 +02:00
parent c21e7e7f30
commit 5f168f8020
21 changed files with 780 additions and 815 deletions

View file

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

View file

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

View file

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

View file

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

View file

@ -1,6 +1,7 @@
#include <stdlib.h>
#include <timer.h>
#include <dynamic.h>
#include <console.h>
#include "../trainscript/tsvm.hpp"
using namespace ker;

View file

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

View file

@ -41,6 +41,7 @@ struct ParserData
}
}
die_extra("ParserData::strdup", "out of strings");
return nullptr;
}
};

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

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

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

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

View file

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

View file

@ -1,6 +1,7 @@
%{
#include <stdio.h>
#include <stdlib.h>
#include <console.h>
#include "common.h"

View file

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

View file

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