Variable refactoring pt.1 - Change union to pointer.

This commit is contained in:
Felix Queissner 2015-08-19 19:07:49 +02:00
parent 5f168f8020
commit 996bef56a6
12 changed files with 258 additions and 105 deletions

View file

@ -2,7 +2,7 @@
Artifact=kernel
TempDir=obj
SourceDir=asm src
Files=trainscript/trainscript.y trainscript/trainscript.l trainscript/tsvm.cpp
Files=trainscript/trainscript.y trainscript/trainscript.l trainscript/tsvm.cpp trainscript/variable.cpp
AdditionalObjects=obj/main.o
LexUseCpp

View file

@ -12,8 +12,8 @@ YACC = bison
# File Lists
SRCS_AS = asm/dynamic.S asm/intr_common_handler.S asm/multiboot.S asm/start.S
SRCS_CC = src/console.c src/init.c src/interrupts.c src/malloc.c src/pmm.c src/serial.c src/stdlib.c src/timer.c src/vmm.c
SRCS_CXX = trainscript/tsvm.cpp src/cplusplus.cpp src/vm.cpp obj/trainscript.yy.cpp obj/trainscript.tab.cpp
OBJS = obj/tsvm.o obj/dynamic.o obj/intr_common_handler.o obj/multiboot.o obj/start.o obj/console.o obj/init.o obj/interrupts.o obj/malloc.o obj/pmm.o obj/serial.o obj/stdlib.o obj/timer.o obj/vmm.o obj/cplusplus.o obj/vm.o obj/trainscript.yy.o obj/trainscript.tab.o obj/main.o
SRCS_CXX = trainscript/tsvm.cpp trainscript/variable.cpp src/cplusplus.cpp src/vm.cpp obj/trainscript.yy.cpp obj/trainscript.tab.cpp
OBJS = obj/tsvm.o obj/variable.o obj/dynamic.o obj/intr_common_handler.o obj/multiboot.o obj/start.o obj/console.o obj/init.o obj/interrupts.o obj/malloc.o obj/pmm.o obj/serial.o obj/stdlib.o obj/timer.o obj/vmm.o obj/cplusplus.o obj/vm.o obj/trainscript.yy.o obj/trainscript.tab.o obj/main.o
# Flags
FLAGS = -m32 -Dnullptr=0 -D__cdecl="__attribute__((cdecl))" -mno-sse -mno-sse2 -mno-mmx
@ -27,10 +27,10 @@ all: kernel
.PHONY: clean
clean:
$(RM) obj/trainscript.yy.cpp obj/trainscript.tab.cpp obj/tsvm.o obj/dynamic.o obj/intr_common_handler.o obj/multiboot.o obj/start.o obj/console.o obj/init.o obj/interrupts.o obj/malloc.o obj/pmm.o obj/serial.o obj/stdlib.o obj/timer.o obj/vmm.o obj/cplusplus.o obj/vm.o obj/trainscript.yy.o obj/trainscript.tab.o obj/main.o
$(RM) obj/trainscript.yy.cpp obj/trainscript.tab.cpp obj/tsvm.o obj/variable.o obj/dynamic.o obj/intr_common_handler.o obj/multiboot.o obj/start.o obj/console.o obj/init.o obj/interrupts.o obj/malloc.o obj/pmm.o obj/serial.o obj/stdlib.o obj/timer.o obj/vmm.o obj/cplusplus.o obj/vm.o obj/trainscript.yy.o obj/trainscript.tab.o obj/main.o
kernel: obj/tsvm.o obj/dynamic.o obj/intr_common_handler.o obj/multiboot.o obj/start.o obj/console.o obj/init.o obj/interrupts.o obj/malloc.o obj/pmm.o obj/serial.o obj/stdlib.o obj/timer.o obj/vmm.o obj/cplusplus.o obj/vm.o obj/trainscript.yy.o obj/trainscript.tab.o obj/main.o
$(LD) $(FLAGS) $(LDFLAGS) -o $@ obj/tsvm.o obj/dynamic.o obj/intr_common_handler.o obj/multiboot.o obj/start.o obj/console.o obj/init.o obj/interrupts.o obj/malloc.o obj/pmm.o obj/serial.o obj/stdlib.o obj/timer.o obj/vmm.o obj/cplusplus.o obj/vm.o obj/trainscript.yy.o obj/trainscript.tab.o obj/main.o
kernel: obj/tsvm.o obj/variable.o obj/dynamic.o obj/intr_common_handler.o obj/multiboot.o obj/start.o obj/console.o obj/init.o obj/interrupts.o obj/malloc.o obj/pmm.o obj/serial.o obj/stdlib.o obj/timer.o obj/vmm.o obj/cplusplus.o obj/vm.o obj/trainscript.yy.o obj/trainscript.tab.o obj/main.o
$(LD) $(FLAGS) $(LDFLAGS) -o $@ obj/tsvm.o obj/variable.o obj/dynamic.o obj/intr_common_handler.o obj/multiboot.o obj/start.o obj/console.o obj/init.o obj/interrupts.o obj/malloc.o obj/pmm.o obj/serial.o obj/stdlib.o obj/timer.o obj/vmm.o obj/cplusplus.o obj/vm.o obj/trainscript.yy.o obj/trainscript.tab.o obj/main.o
# src/console.c
obj/console.o: src/console.c include/console.h include/stdlib.h \
@ -96,6 +96,13 @@ obj/tsvm.o: trainscript/tsvm.cpp include/stdlib.h include/varargs.h \
obj/trainscript.tab.hpp trainscript/trainscript.l.h include/string.h
$(CXX) -iquoteobj $(FLAGS) $(CXXFLAGS) -o $@ -c trainscript/tsvm.cpp
# trainscript/variable.cpp
obj/variable.o: trainscript/variable.cpp include/kernel.h \
trainscript/variable.hpp trainscript/type.hpp trainscript/types.hpp \
include/ker/string.hpp include/stdlib.h include/varargs.h \
include/config.h include/malloc.h trainscript/typeid.hpp
$(CXX) -iquoteobj $(FLAGS) $(CXXFLAGS) -o $@ -c trainscript/variable.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

View file

@ -48,7 +48,26 @@ It also features a surveillance mode that logs all allocations / frees. It can b
|0x20 - 0x27| IRQ0 - IRQ7 |
|0x28 - 0x2F| IRQ8 - IRQ15 |
###
## Virtual Machine Architecture
### Type Format
The virtual machine supports 5 base types:
| Name | Description | Size |
|---------|------------------------------------------------|--------|
| Void | A type that specifies a non-existent value. | 0 |
| Int | An integral number. | 32 |
| Real | A decimal number. | 32/64¹ |
| Text | A string value containing a sequence of bytes. | any² |
| Bool | A boolen value, either `true` or `false`. | 1³ |
¹) A real number is either a float (32 bits) or a double (64 bits) depending on the configuration in `types.hpp`.
²) A string value is sized the length of the string in bytes plus the size of the length specifier (32 bits).
³) A boolean is representet as a 1 bit value but due to architecture limitations it is stored with 32 bits.
### Variable Format
Variables are stored in a `type+pointer` format where `type` stores the type of the variable and `pointer` either a pointer to the variable value or, if `type` is a pointer type, the pointer value of the variable.
## OS Architecture
To be done.

View file

@ -82,33 +82,22 @@ Variable NativeMethod::invoke(Vector<Variable> arguments)
// Copy arguments
size_t stackSize = 0;
for(size_t i = 0; i < arguments.length(); i++) {
if(arguments[i].type != this->parameters[i]) {
if(arguments[i].type() != this->parameters[i]) {
// die_extra("NativeMethod.InvalidArgumentType", arguments[i].type.name());
}
switch(arguments[i].type.id) {
case TypeID::Bool: stackSize += sizeof(Int); break;
case TypeID::Int: stackSize += sizeof(Int); break;
case TypeID::Real: stackSize += sizeof(Real); break;
default: die_extra("NativeMethod.InvalidArgument", arguments[i].type.name()); break;
}
stackSize += arguments[i].type().size();
}
uint8_t *stack = (uint8_t*)malloc(stackSize);
uint8_t *stackPtr = stack;
for(size_t i = 0; i < arguments.length(); i++) {
switch(arguments[i].type.id) {
case TypeID::Bool:
*reinterpret_cast<Int*>(stackPtr) = arguments[i].boolean ? 1 : 0;
stackPtr += sizeof(Int);
break;
case TypeID::Int:
*reinterpret_cast<Int*>(stackPtr) = arguments[i].integer;
stackPtr += sizeof(Int);
break;
case TypeID::Real:
*reinterpret_cast<Real*>(stackPtr) = arguments[i].real;
stackPtr += sizeof(Real);
break;
size_t size = arguments[i].type().size();
void *data = arguments[i].data();
if(size > 0) {
memcpy(stackPtr, data, size);
stackPtr += size;
}
}
@ -116,7 +105,7 @@ Variable NativeMethod::invoke(Vector<Variable> arguments)
free(stack);
return mkvar((Int)0);
return Type::Int.createInstance();
}
extern "C" void __cdecl printInt(int i) {

View file

@ -16,7 +16,8 @@ SOURCES += \
src/timer.c \
src/cplusplus.cpp \
src/vm.cpp \
src/serial.c
src/serial.c \
trainscript/variable.cpp
HEADERS += \
include/console.h \

View file

@ -49,7 +49,7 @@ struct ParserData
struct VariableDeclaration
{
char *name;
trainscript::Variable variable;
trainscript::Type type;
};
struct LocalVariable

View file

@ -46,7 +46,7 @@ namespace trainscript
}
Type expectedResult(ExecutionContext &) const override {
return this->value.type;
return this->value.type();
}
};
@ -79,7 +79,7 @@ namespace trainscript
if(var == nullptr) {
return Type::Invalid;
} else {
return var->type;
return var->type();
}
}
};
@ -109,18 +109,13 @@ namespace trainscript
die_extra("VariableAssignmentExpression.VariableNotFound", this->variableName.str());
}
if(target->type != result.type) {
die_extra("VariableAssignmentExpression.ExpectedType", result.type.name());
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;
}
*target = result;
return result;
return *target;
}
bool validate(ExecutionContext &context, ker::String &errorCode) const override {
@ -143,7 +138,7 @@ namespace trainscript
errorCode = "Variable " + this->variableName + " not found.";
return false;
}
if(var->type != result) {
if(var->type() != result) {
errorCode = "Variable assignment has invalid type.";
return false;
}
@ -248,14 +243,14 @@ namespace trainscript
Variable left = this->lhs->execute(context);
Variable right = this->rhs->execute(context);
if(left.type != right.type) {
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());
if(result.type().usable() == false) {
die_extra("ArithmeticExpression.InvalidResult", result.type().name());
}
return result;
}
@ -324,13 +319,13 @@ namespace trainscript
}
Variable result = this->condition->execute(context);
if(result.type != Type::Boolean) {
die_extra("IfExpression.TypeMismatch", result.type.name());
if(result.type() != Type::Bool) {
die_extra("IfExpression.TypeMismatch", result.type().name());
}
if((result.boolean == true) && (this->blockTrue != nullptr)) {
if((result.value<Bool>() == true) && (this->blockTrue != nullptr)) {
this->blockTrue->execute(context);
}
if((result.boolean == false) && (this->blockFalse != nullptr)) {
if((result.value<Bool>() == false) && (this->blockFalse != nullptr)) {
this->blockFalse->execute(context);
}
return Variable::Void;
@ -389,10 +384,10 @@ namespace trainscript
while(true)
{
Variable cond = this->condition->execute(context);
if(cond.type != Type::Boolean) {
if(cond.type() != Type::Bool) {
return Variable::Invalid;
}
if(cond.boolean == false) {
if(cond.value<Bool>() == false) {
break;
}

View file

@ -145,7 +145,7 @@ void yyerror(void *scanner, const char *s);
input:
%empty
| input variableDeclaration SEMICOLON {
auto *var = new Variable($2.variable);
auto *var = new Variable($2.type.createInstance());
context->module->variables.add( ker::String($2.name), var );
}
| input method {
@ -328,9 +328,9 @@ elseIfLoop:
;
expression:
INT { $$ = new ConstantExpression(mkvar($1)); }
| REAL { $$ = new ConstantExpression(mkvar($1)); }
// | TEXT { $$ = new ConstantExpression(mkvar($1)); }
INT { $$ = new ConstantExpression(Variable::fromInt($1)); }
| REAL { $$ = new ConstantExpression(Variable::fromReal($1)); }
// | TEXT { $$ = new ConstantExpression(Variable::fromText($1)); }
| IDENTIFIER { $$ = new VariableExpression($1); }
| IDENTIFIER LBRACKET expressionList RBRACKET {
auto *call = new MethodInvokeExpression($1);
@ -379,8 +379,7 @@ expressionList:
variableDeclaration:
KW_VAR IDENTIFIER COLON typeName {
$$.name = $2;
$$.variable.type = $4;
$$.variable.integer = 0; // Initialize with zeroes
$$.type = $4;
}
;

View file

@ -15,14 +15,14 @@ namespace trainscript
const Type Type::Int = { TypeID::Int, 0 };
const Type Type::Real = { TypeID::Real, 0 };
const Type Type::Text = { TypeID::Text, 0 };
const Type Type::Boolean = { TypeID::Bool, 0 };
const Type Type::Bool = { TypeID::Bool, 0 };
const Variable Variable::Invalid = { Type::Invalid, 0 };
const Variable Variable::Void = { Type::Void, 0 };
const Variable Variable::Int = { Type::Int, 0 };
const Variable Variable::Real = { Type::Real, 0 };
const Variable Variable::Text = { Type::Text, 0 };
const Variable Variable::Boolean = { Type::Boolean, 0 };
const Variable Variable::Bool = { Type::Bool, 0 };
bool Module::validate(ker::String &errorCode) const
{
@ -101,9 +101,7 @@ namespace trainscript
context.add(var.first, var.second);
}
Variable returnVariable = {
this->mReturnValue.second, 0
};
Variable returnVariable = this->mReturnValue.second.createInstance();
if(this->mReturnValue.second.usable()) {
context.add(this->mReturnValue.first, &returnVariable);
@ -112,7 +110,7 @@ namespace trainscript
die_extra("ScriptMethod::invoke", "Invalid argument count.");
}
for(size_t i = 0; i < this->mArguments.length(); i++) {
if(this->mArguments[i].second != arguments[i].type) {
if(this->mArguments[i].second != arguments[i].type()) {
die_extra("ScriptMethod::invoke", "Invalid argument type.");
}
auto *v = new Variable(arguments[i]);
@ -120,7 +118,7 @@ namespace trainscript
context.add(this->mArguments[i].first, v);
}
for(auto local : this->mLocals) {
auto *v = new Variable { local.second, 0 };
auto *v = new Variable(local.second.createInstance());
temporaries.append(v);
context.add(local.first, v);
}
@ -148,9 +146,7 @@ namespace trainscript
context.add(var.first, var.second);
}
Variable returnVariable = {
this->mReturnValue.second, 0
};
Variable returnVariable = this->mReturnValue.second.createInstance();
if(this->mReturnValue.second.usable()) {
if(context.get(this->mReturnValue.first) != nullptr) {
@ -165,14 +161,14 @@ namespace trainscript
errorCode = "Parameter overlaps a variable.";
return false;
}
context.add(this->mArguments[i].first, new Variable { this->mArguments[i].second, 0 });
context.add(this->mArguments[i].first, new Variable(this->mArguments[i].second.createInstance()));
}
for(auto local : this->mLocals) {
if(context.get(local.first) != nullptr) {
errorCode = "Local variable overlaps a variable.";
return false;
}
context.add(local.first, new Variable { local.second, 0 });
context.add(local.first, new Variable(local.second.createInstance()));
}
if(this->block->validate(context, errorCode) == false) {
@ -184,6 +180,7 @@ namespace trainscript
namespace ops
{
/*
Variable add(Variable lhs, Variable rhs)
{
switch(lhs.type.id) {
@ -297,5 +294,7 @@ namespace trainscript
return Variable::Invalid;
}
}
*/
}
}

View file

@ -6,6 +6,8 @@
namespace trainscript
{
class Variable;
struct Type
{
TypeID id;
@ -35,22 +37,49 @@ namespace trainscript
}
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";
if(this->pointer > 0) {
static char str[64];
strcpy(str, this->dereference().name());
strcat(str, "*");
return str;
} else {
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>";
}
}
}
size_t size() const
{
if(this->pointer > 0) {
return sizeof(void*);
} else {
switch(id) {
case TypeID::Invalid: return 0;
case TypeID::Void: return 0;
case TypeID::Int: return sizeof(Int);
case TypeID::Real: return sizeof(Real);
case TypeID::Text: return sizeof(Text);
case TypeID::Bool: return sizeof(Bool);
default: die("Type::size.UnknownTypeID");
}
}
return 0;
}
Variable createInstance() const;
static const Type Invalid;
static const Type Void;
static const Type Int;
static const Type Real;
static const Type Text;
static const Type Boolean;
static const Type Bool;
};
}

77
trainscript/variable.cpp Normal file
View file

@ -0,0 +1,77 @@
#include <kernel.h>
#include "variable.hpp"
namespace trainscript
{
Variable::~Variable()
{
// Only free non-pointer types.
if(this->mType.pointer == 0) {
switch(this->mType.id) {
#define DELETE(type) case TypeID::type: \
delete reinterpret_cast<trainscript::type*>(this->mValue); \
break
DELETE(Bool);
DELETE(Int);
DELETE(Real);
DELETE(Text);
#undef DELETE
}
}
}
Variable &Variable::operator =(const Variable &other)
{
if(this->mType != other.mType) {
die_extra("Variable.operator=", "Type mismatch.");
}
if(this->mType.pointer == 0) {
switch(this->mType.id) {
#define COPY(type) case TypeID::type: \
*reinterpret_cast<trainscript::type*>(this->mValue) = *reinterpret_cast<trainscript::type*>(other.mValue); \
break
COPY(Bool);
COPY(Int);
COPY(Real);
COPY(Text);
#undef COPY
}
} else {
this->mValue = other.mValue;
}
return *this;
}
Variable Type::createInstance() const
{
void *value = nullptr;
if(this->pointer > 0) {
switch(this->id) {
#define CREATE(type, initial) case TypeID::type: \
value = new trainscript::type(initial); \
break
CREATE(Bool, false);
CREATE(Int, 0);
CREATE(Real, 0.0);
CREATE(Text, "");
default:
die("Type::createInstance.InvalidTypeID");
}
}
return Variable(*this, value);
}
#define fromType(type) Variable Variable::from##type(trainscript::type i) \
{ \
Variable var = Type::type.createInstance(); \
var.value<trainscript::type>() = i; \
return var; \
}
fromType(Int)
fromType(Real)
fromType(Text)
fromType(Bool)
}

View file

@ -5,15 +5,66 @@
namespace trainscript
{
struct Variable
class Variable
{
Type type;
union {
Int integer;
Real real;
// Text text;
Bool boolean;
};
friend class Type;
private:
Type mType;
void *mValue;
private:
// To be called by Type::createInstance
Variable(const Type &type, void *value) :
mType(type),
mValue(value)
{
}
public:
Variable() :
mType(Type::Invalid),
mValue(nullptr)
{
}
Variable(const Variable &other) :
mType(other.mType),
mValue(nullptr)
{
*this = other;
}
Variable(Variable &&other) :
mType(other.mType),
mValue(other.mValue)
{
other.mType = Type::Invalid;
other.mValue = nullptr;
}
~Variable();
Variable &operator =(const Variable &other);
void *data() const
{
return this->mValue;
}
template<typename T>
T& value()
{
return *reinterpret_cast<T*>(this->mValue);
}
template<typename T>
const T& value() const
{
return *reinterpret_cast<T*>(this->mValue);
}
const Type &type() const {
return this->mType;
}
#if defined(TSVM_PRINTVAL)
void printval() const
@ -27,29 +78,16 @@ namespace trainscript
}
#endif
static Variable fromInt(trainscript::Int i);
static Variable fromReal(trainscript::Real r);
static Variable fromText(trainscript::Text t);
static Variable fromBool(Bool b);
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 const Variable Bool;
};
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;
}
}