Adds semantic language validation.
This commit is contained in:
parent
3e2bf8ff10
commit
620f337bc2
11 changed files with 399 additions and 128 deletions
16
Makefile
16
Makefile
|
@ -77,9 +77,9 @@ obj/vmm.o: src/vmm.c include/vmm.h include/pmm.h include/multiboot.h \
|
|||
obj/tsvm.o: trainscript/tsvm.cpp include/stdlib.h include/varargs.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/ker/pair.hpp trainscript/typeid.hpp \
|
||||
trainscript/trainscript.tab.hpp trainscript/trainscript.l.h \
|
||||
include/string.h
|
||||
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
|
||||
|
||||
# src/cplusplus.cpp
|
||||
|
@ -91,7 +91,7 @@ obj/cplusplus.o: src/cplusplus.cpp include/stdlib.h include/varargs.h \
|
|||
obj/vm.o: src/vm.cpp include/stdlib.h include/varargs.h include/timer.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/ker/pair.hpp src/../trainscript/typeid.hpp
|
||||
include/kernel.h include/ker/pair.hpp src/../trainscript/typeid.hpp
|
||||
$(CXX) $(FLAGS) $(CXXFLAGS) -o $@ -c src/vm.cpp
|
||||
|
||||
# obj/trainscript.yy.cpp
|
||||
|
@ -99,15 +99,17 @@ obj/trainscript.yy.o: obj/trainscript.yy.cpp include/string.h \
|
|||
include/stdlib.h include/varargs.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/ker/pair.hpp trainscript/typeid.hpp obj/trainscript.tab.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
|
||||
|
||||
# obj/trainscript.tab.cpp
|
||||
obj/trainscript.tab.o: obj/trainscript.tab.cpp include/stdlib.h \
|
||||
include/varargs.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/ker/pair.hpp \
|
||||
trainscript/typeid.hpp trainscript/trainscript.l.h include/string.h
|
||||
include/ker/new.hpp include/ker/dictionary.hpp include/kernel.h \
|
||||
include/ker/pair.hpp trainscript/typeid.hpp trainscript/trainscript.l.h \
|
||||
include/string.h
|
||||
$(CXX) -iquotetrainscript $(FLAGS) $(CXXFLAGS) -o $@ -c obj/trainscript.tab.cpp
|
||||
|
||||
# asm/intr_common_handler.S
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <kernel.h>
|
||||
|
||||
#include "pair.hpp"
|
||||
#include "vector.hpp"
|
||||
|
||||
|
@ -10,6 +12,7 @@ namespace ker
|
|||
{
|
||||
public:
|
||||
typedef Pair<Key, Value> Entry;
|
||||
static Value _default;
|
||||
public:
|
||||
Vector<Entry> contents;
|
||||
public:
|
||||
|
@ -45,16 +48,21 @@ namespace ker
|
|||
return pair.second;
|
||||
}
|
||||
}
|
||||
die("Key not found in dictionary.");
|
||||
return _default;
|
||||
}
|
||||
|
||||
const Value &at(const Key &key) const
|
||||
{
|
||||
static Value _default;
|
||||
for(auto &&pair : this->contents)
|
||||
{
|
||||
if(pair.first == key) {
|
||||
return pair.second;
|
||||
}
|
||||
}
|
||||
die("Key not found in dictionary.");
|
||||
return _default;
|
||||
}
|
||||
|
||||
Value get(const Key &key) const
|
||||
|
@ -122,4 +130,7 @@ namespace ker
|
|||
return this->contents.end();
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Key, typename Value>
|
||||
Value Dictionary<Key, Value>::_default = Value();
|
||||
}
|
||||
|
|
|
@ -32,12 +32,6 @@ namespace ker
|
|||
other.mLength = 0;
|
||||
}
|
||||
|
||||
String & operator = (const String &other)
|
||||
{
|
||||
this->copyFrom(other.mText, other.mLength);
|
||||
return *this;
|
||||
}
|
||||
|
||||
String(const char *text) :
|
||||
mText(nullptr),
|
||||
mLength(0)
|
||||
|
@ -52,6 +46,12 @@ namespace ker
|
|||
this->copyFrom(bytes, length);
|
||||
}
|
||||
|
||||
String & operator = (const String &other)
|
||||
{
|
||||
this->copyFrom(other.mText, other.mLength);
|
||||
return *this;
|
||||
}
|
||||
|
||||
~String()
|
||||
{
|
||||
if(this->mText != nullptr) {
|
||||
|
@ -72,6 +72,21 @@ namespace ker
|
|||
return memcmp(this->mText, other.mText, this->mLength) == 0;
|
||||
}
|
||||
|
||||
String append(const String &other) const
|
||||
{
|
||||
uint8_t *data = (uint8_t*)malloc(this->mLength + other.mLength);
|
||||
memcpy(&data[0], this->mText, this->mLength);
|
||||
memcpy(&data[this->mLength], other.mText, other.mLength);
|
||||
String cat(data, this->mLength + other.mLength);
|
||||
free(data);
|
||||
return cat;
|
||||
}
|
||||
|
||||
static String concat(const String &lhs, const String &rhs)
|
||||
{
|
||||
return lhs.append(rhs);
|
||||
}
|
||||
|
||||
const uint8_t *text() const
|
||||
{
|
||||
static const uint8_t empty[] = { 0 };
|
||||
|
@ -128,4 +143,15 @@ namespace ker
|
|||
this->mText[this->mLength] = 0; // last byte is always 0
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
static String operator + (const char *lhs, const String &rhs)
|
||||
{
|
||||
return String::concat(lhs, rhs);
|
||||
}
|
||||
|
||||
static String operator + (const String &lhs, const char *rhs)
|
||||
{
|
||||
return String::concat(lhs, rhs);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -2,65 +2,10 @@ VAR global : INT;
|
|||
|
||||
PUB main() | i : INT
|
||||
BEGIN
|
||||
afraid(15, 34) → i;
|
||||
0 -> i;
|
||||
WHILE i < 5 DO
|
||||
BEGIN
|
||||
print(i);
|
||||
i + 1 -> i;
|
||||
fun(1) -> i;
|
||||
END
|
||||
|
||||
print(factorial(4), fibonacci(8), problem1(10));
|
||||
|
||||
REPEAT
|
||||
PUB fun() -> i : INT
|
||||
BEGIN
|
||||
print(1);
|
||||
sleep'(5);
|
||||
END
|
||||
END
|
||||
|
||||
# Sleep implementation
|
||||
PRI sleep'(time : INT) | init : INT
|
||||
BEGIN
|
||||
timer_get() → init;
|
||||
WHILE (timer_get() - init) < time DO
|
||||
0; # do nothing
|
||||
END
|
||||
|
||||
# Calculates factorial (number!)
|
||||
PRI factorial(number : INT) → result : INT
|
||||
BEGIN
|
||||
IF number > 1 THEN
|
||||
number * factorial(number - 1) → result;
|
||||
ELSE
|
||||
1 → result;
|
||||
END
|
||||
|
||||
# Recursive test
|
||||
PRI fibonacci(n : INT) → f : INT
|
||||
BEGIN
|
||||
IF n = 0 THEN
|
||||
0 → f;
|
||||
ELSEIF n = 1 THEN
|
||||
1 → f;
|
||||
ELSE
|
||||
fibonacci(n - 1) + fibonacci(n - 2) → f;
|
||||
END
|
||||
|
||||
# If we list all the natural numbers below 10 that are
|
||||
# multiples of 3 or 5, we get 3, 5, 6 and 9. The sum of
|
||||
# these multiples is 23.
|
||||
# Find the sum of all the multiples of 3 or 5 below 1000.
|
||||
PRI problem1(max : INT) → r : INT | iter : INT
|
||||
BEGIN
|
||||
1 → iter;
|
||||
0 → r;
|
||||
WHILE iter < max DO
|
||||
BEGIN
|
||||
IF (iter % 5) = 0 THEN
|
||||
r + iter → r;
|
||||
ELSEIF (iter % 3) = 0 THEN
|
||||
r + iter → r;
|
||||
iter + 1 → iter;
|
||||
END
|
||||
10 -> i;
|
||||
END
|
||||
|
|
|
@ -183,7 +183,6 @@ void kprintf(const char *format, ...)
|
|||
{
|
||||
c = *(format++);
|
||||
int i;
|
||||
float f;
|
||||
char *str;
|
||||
switch(c)
|
||||
{
|
||||
|
|
37
src/vm.cpp
37
src/vm.cpp
|
@ -13,13 +13,6 @@ extern "C" {
|
|||
|
||||
void cpp_test();
|
||||
|
||||
class PrintMethod :
|
||||
public Method
|
||||
{
|
||||
public:
|
||||
Variable invoke(Vector<Variable> arguments) override;
|
||||
};
|
||||
|
||||
class NativeMethod :
|
||||
public Method
|
||||
{
|
||||
|
@ -33,19 +26,25 @@ public:
|
|||
}
|
||||
|
||||
Variable invoke(Vector<Variable> arguments) override;
|
||||
};
|
||||
|
||||
Variable PrintMethod::invoke(Vector<Variable> arguments)
|
||||
bool validate(ker::String &errorCode) const
|
||||
{
|
||||
for(auto &var : arguments)
|
||||
{
|
||||
var.printval();
|
||||
kprintf(" ");
|
||||
if(this->function == nullptr) {
|
||||
errorCode = "Native method with nullptr interface.";
|
||||
return false;
|
||||
}
|
||||
kprintf("\n");
|
||||
return Variable::Void;
|
||||
return true;
|
||||
}
|
||||
|
||||
Vector<Type> arguments() const {
|
||||
return Vector<Type>();
|
||||
}
|
||||
|
||||
Type returnType() const {
|
||||
return Type::Int;
|
||||
}
|
||||
};
|
||||
|
||||
static void copyCode(uint8_t **ptr, const uint8_t *src, size_t length)
|
||||
{
|
||||
for(size_t i = 0; i < length; i++) {
|
||||
|
@ -169,9 +168,11 @@ extern "C" void vm_start()
|
|||
|
||||
kprintf("Module successfully loaded :)\n");
|
||||
|
||||
module->methods.add("print", new PrintMethod());
|
||||
|
||||
module->methods.add("afraid", new NativeMethod(reinterpret_cast<void*>(&cCodeFunction)));
|
||||
String errorCode;
|
||||
if(module->validate(errorCode) == false) {
|
||||
kprintf("Module validation failed: %s\n", errorCode.str());
|
||||
return;
|
||||
}
|
||||
|
||||
NativeModuleDef *mod = methods;
|
||||
while(mod->name != nullptr) {
|
||||
|
|
|
@ -39,7 +39,7 @@ struct VariableDeclaration
|
|||
struct LocalVariable
|
||||
{
|
||||
char *name;
|
||||
trainscript::Variable variable;
|
||||
trainscript::Type type;
|
||||
LocalVariable *next;
|
||||
};
|
||||
|
||||
|
|
|
@ -1,9 +1,66 @@
|
|||
VAR global : INT;
|
||||
|
||||
VAR res : INT;
|
||||
|
||||
PUB main(x : INT) → c : INT
|
||||
PUB main() | i : INT
|
||||
BEGIN
|
||||
# factorial(10) → res;
|
||||
# fibonacci(7) → res;
|
||||
problem1(1000) → res;
|
||||
afraid(15, 34) → i;
|
||||
0 -> i;
|
||||
WHILE i < 5 DO
|
||||
BEGIN
|
||||
print(i);
|
||||
i + 1 -> i;
|
||||
END
|
||||
|
||||
print(factorial(4), fibonacci(8), problem1(10));
|
||||
|
||||
REPEAT
|
||||
BEGIN
|
||||
print(1);
|
||||
sleep'(5);
|
||||
END
|
||||
END
|
||||
|
||||
# Sleep implementation
|
||||
PRI sleep'(time : INT) | init : INT
|
||||
BEGIN
|
||||
timer_get() → init;
|
||||
WHILE (timer_get() - init) < time DO
|
||||
0; # do nothing
|
||||
END
|
||||
|
||||
# Calculates factorial (number!)
|
||||
PRI factorial(number : INT) → result : INT
|
||||
BEGIN
|
||||
IF number > 1 THEN
|
||||
number * factorial(number - 1) → result;
|
||||
ELSE
|
||||
1 → result;
|
||||
END
|
||||
|
||||
# Recursive test
|
||||
PRI fibonacci(n : INT) → f : INT
|
||||
BEGIN
|
||||
IF n = 0 THEN
|
||||
0 → f;
|
||||
ELSEIF n = 1 THEN
|
||||
1 → f;
|
||||
ELSE
|
||||
fibonacci(n - 1) + fibonacci(n - 2) → f;
|
||||
END
|
||||
|
||||
# If we list all the natural numbers below 10 that are
|
||||
# multiples of 3 or 5, we get 3, 5, 6 and 9. The sum of
|
||||
# these multiples is 23.
|
||||
# Find the sum of all the multiples of 3 or 5 below 1000.
|
||||
PRI problem1(max : INT) → r : INT | iter : INT
|
||||
BEGIN
|
||||
1 → iter;
|
||||
0 → r;
|
||||
WHILE iter < max DO
|
||||
BEGIN
|
||||
IF (iter % 5) = 0 THEN
|
||||
r + iter → r;
|
||||
ELSEIF (iter % 3) = 0 THEN
|
||||
r + iter → r;
|
||||
iter + 1 → iter;
|
||||
END
|
||||
END
|
||||
|
|
|
@ -154,18 +154,18 @@ input:
|
|||
ScriptMethod *method = new ScriptMethod(mod, $2.body);
|
||||
method->isPublic = $2.header.isPublic;
|
||||
if($2.header.returnValue) {
|
||||
method->returnValue = ker::Pair<ker::String, Variable>(
|
||||
method->mReturnValue = ker::Pair<ker::String, Type>(
|
||||
$2.header.returnValue->name,
|
||||
$2.header.returnValue->variable);
|
||||
$2.header.returnValue->type);
|
||||
}
|
||||
LocalVariable *local = $2.header.locals;
|
||||
while(local) {
|
||||
method->locals.add( local->name, local->variable );
|
||||
method->mLocals.add( local->name, local->type );
|
||||
local = local->next;
|
||||
}
|
||||
LocalVariable *arg = $2.header.arguments;
|
||||
while(arg) {
|
||||
method->arguments.append( { arg->name, arg->variable } );
|
||||
method->mArguments.append( { arg->name, arg->type} );
|
||||
arg = arg->next;
|
||||
}
|
||||
|
||||
|
@ -277,8 +277,7 @@ argument:
|
|||
IDENTIFIER COLON typeName {
|
||||
$$ = new LocalVariable();
|
||||
$$->name = $1;
|
||||
$$->variable.type = $3;
|
||||
$$->variable.integer = 0; // zero value
|
||||
$$->type = $3;
|
||||
$$->next = nullptr;
|
||||
}
|
||||
;
|
||||
|
|
|
@ -28,6 +28,17 @@ namespace trainscript
|
|||
const Variable Variable::Text = { Type::Text, 0 };
|
||||
const Variable Variable::Boolean = { Type::Boolean, 0 };
|
||||
|
||||
bool Module::validate(ker::String &errorCode) const
|
||||
{
|
||||
errorCode = "";
|
||||
for(auto method : this->methods) {
|
||||
if(method.second->validate(errorCode) == false) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Module *VM::load(const void *buffer, size_t length)
|
||||
{
|
||||
void *internalStorage = malloc(length);
|
||||
|
@ -85,28 +96,78 @@ namespace trainscript
|
|||
context.add(var.first, var.second);
|
||||
}
|
||||
|
||||
if(this->returnValue.second.type.usable()) {
|
||||
context.add(this->returnValue.first, &this->returnValue.second);
|
||||
Variable returnVariable = {
|
||||
this->mReturnValue.second, 0
|
||||
};
|
||||
|
||||
if(this->mReturnValue.second.usable()) {
|
||||
context.add(this->mReturnValue.first, &returnVariable);
|
||||
}
|
||||
if(arguments.length() != this->arguments.length()) {
|
||||
if(arguments.length() != this->mArguments.length()) {
|
||||
return Variable::Invalid;
|
||||
}
|
||||
for(size_t i = 0; i < this->arguments.length(); i++) {
|
||||
if(this->arguments[i].second.type != arguments[i].type) {
|
||||
for(size_t i = 0; i < this->mArguments.length(); i++) {
|
||||
if(this->mArguments[i].second != arguments[i].type) {
|
||||
return Variable::Invalid;
|
||||
}
|
||||
context.add(this->arguments[i].first, new Variable(arguments[i]));
|
||||
context.add(this->mArguments[i].first, new Variable(arguments[i]));
|
||||
}
|
||||
for(auto local : this->locals) {
|
||||
context.add(local.first, new Variable(local.second));
|
||||
for(auto local : this->mLocals) {
|
||||
context.add(local.first, new Variable { local.second, 0 });
|
||||
}
|
||||
|
||||
this->block->execute(context);
|
||||
|
||||
return this->returnValue.second;
|
||||
return returnVariable;
|
||||
}
|
||||
|
||||
bool ScriptMethod::validate(ker::String &errorCode) const
|
||||
{
|
||||
if(this->block == nullptr) {
|
||||
errorCode = "Method block is not set.";
|
||||
return false;
|
||||
}
|
||||
|
||||
LocalContext context(this->module);
|
||||
|
||||
for(auto var : this->module->variables)
|
||||
{
|
||||
context.add(var.first, var.second);
|
||||
}
|
||||
|
||||
Variable returnVariable = {
|
||||
this->mReturnValue.second, 0
|
||||
};
|
||||
|
||||
if(this->mReturnValue.second.usable()) {
|
||||
if(context.get(this->mReturnValue.first) != nullptr) {
|
||||
errorCode = "Return variable overlaps a variable.";
|
||||
return false;
|
||||
}
|
||||
context.add(this->mReturnValue.first, &returnVariable);
|
||||
}
|
||||
|
||||
for(size_t i = 0; i < this->mArguments.length(); i++) {
|
||||
if(context.get(this->mArguments[i].first) != nullptr) {
|
||||
errorCode = "Parameter overlaps a variable.";
|
||||
return false;
|
||||
}
|
||||
context.add(this->mArguments[i].first, new Variable { this->mArguments[i].second, 0 });
|
||||
}
|
||||
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 });
|
||||
}
|
||||
|
||||
if(this->block->validate(context, errorCode) == false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
namespace ops
|
||||
{
|
||||
|
|
|
@ -5,15 +5,10 @@ extern "C" {
|
|||
#include <console.h>
|
||||
}
|
||||
|
||||
// #include <map>
|
||||
|
||||
#include <ker/string.hpp>
|
||||
#include <ker/vector.hpp>
|
||||
#include <ker/dictionary.hpp>
|
||||
|
||||
// #include <vector>
|
||||
// #include <string.h>
|
||||
|
||||
#include "typeid.hpp"
|
||||
|
||||
namespace trainscript
|
||||
|
@ -144,6 +139,15 @@ namespace trainscript
|
|||
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 :
|
||||
|
@ -156,6 +160,15 @@ namespace trainscript
|
|||
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);
|
||||
|
@ -168,7 +181,14 @@ namespace trainscript
|
|||
{
|
||||
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 :
|
||||
|
@ -178,9 +198,9 @@ namespace trainscript
|
|||
Module *module;
|
||||
Instruction *block;
|
||||
bool isPublic;
|
||||
ker::Vector<ker::Pair<ker::String, Variable>> arguments;
|
||||
ker::Dictionary<ker::String, Variable> locals;
|
||||
ker::Pair<ker::String, Variable> returnValue;
|
||||
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)
|
||||
{
|
||||
|
@ -188,6 +208,22 @@ namespace trainscript
|
|||
}
|
||||
|
||||
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
|
||||
|
@ -208,6 +244,8 @@ namespace trainscript
|
|||
{
|
||||
return this->variables.get(name);
|
||||
}
|
||||
|
||||
bool validate(ker::String &errorCode) const;
|
||||
};
|
||||
|
||||
class VM
|
||||
|
@ -231,9 +269,13 @@ namespace trainscript
|
|||
Variable value;
|
||||
ConstantExpression(Variable value) : value(value) { }
|
||||
|
||||
Variable execute(LocalContext &context) const override {
|
||||
Variable execute(LocalContext &) const override {
|
||||
return this->value;
|
||||
}
|
||||
|
||||
Type expectedResult(LocalContext &) const override {
|
||||
return this->value.type;
|
||||
}
|
||||
};
|
||||
|
||||
class VariableExpression :
|
||||
|
@ -250,6 +292,24 @@ namespace trainscript
|
|||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
@ -290,6 +350,43 @@ namespace trainscript
|
|||
|
||||
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 :
|
||||
|
@ -320,6 +417,38 @@ namespace trainscript
|
|||
|
||||
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)>
|
||||
|
@ -353,6 +482,47 @@ namespace trainscript
|
|||
|
||||
return OP(left, right);
|
||||
}
|
||||
|
||||
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 :
|
||||
|
|
Loading…
Reference in a new issue