diff --git a/trainscript/Makefile b/trainscript/Makefile index 293dfde..9273a25 100644 --- a/trainscript/Makefile +++ b/trainscript/Makefile @@ -1,5 +1,5 @@ # -fsanitize=address -CFLAGS= +CFLAGS=-g CCFLAGS=$(CFLAGS) -std=c++11 -c LFLAGS=$(CFLAGS) -o tsvm diff --git a/trainscript/file01.ts b/trainscript/file01.ts index b404f18..0c07aef 100644 --- a/trainscript/file01.ts +++ b/trainscript/file01.ts @@ -1,38 +1,8 @@ -# Trainscript Test File -VAR v1' : REAL; -VAR v2' : REAL; +VAR temp : BOOL; -# Execute some stuff PUB main(x : INT) → c : INT - physics(); - factorial(2); - 42 → c; - -# Physics exercise with elastic collision -PRI physics() | m1:REAL, v1:REAL, m2:REAL, v2:REAL - # Wagen 1 - 4.0 → m1; - 1.2 → v1; - - # Wagen 2 - 5.0 → m2; - 0.6 → v2; - - (((m1 - m2) * v1) + (2. * m2 * v2)) / (m1 + m2) → v1'; - (((m2 - m1) * v2) + (2. * m1 * v1)) / (m1 + m2) → v2'; - -# Recursive test -PUB fibonacci(n : INT) → f : INT - #IF n = 0 - 0 → f; - #ELSEIF n = 1 - 1 → f; - #ELSE - fibonacci(n - 1) + fibonacci(n - 2) → f; - -PUB factorial(number : INT) → result : INT - #IF number <= 1 - 1 → result; - #ELSE - number * factorial(number - 1) → result; +BEGIN + (x =/= c) → temp; + x → c; +END diff --git a/trainscript/file02.ts b/trainscript/file02.ts new file mode 100644 index 0000000..b404f18 --- /dev/null +++ b/trainscript/file02.ts @@ -0,0 +1,38 @@ + +# Trainscript Test File +VAR v1' : REAL; +VAR v2' : REAL; + +# Execute some stuff +PUB main(x : INT) → c : INT + physics(); + factorial(2); + 42 → c; + +# Physics exercise with elastic collision +PRI physics() | m1:REAL, v1:REAL, m2:REAL, v2:REAL + # Wagen 1 + 4.0 → m1; + 1.2 → v1; + + # Wagen 2 + 5.0 → m2; + 0.6 → v2; + + (((m1 - m2) * v1) + (2. * m2 * v2)) / (m1 + m2) → v1'; + (((m2 - m1) * v2) + (2. * m1 * v1)) / (m1 + m2) → v2'; + +# Recursive test +PUB fibonacci(n : INT) → f : INT + #IF n = 0 + 0 → f; + #ELSEIF n = 1 + 1 → f; + #ELSE + fibonacci(n - 1) + fibonacci(n - 2) → f; + +PUB factorial(number : INT) → result : INT + #IF number <= 1 + 1 → result; + #ELSE + number * factorial(number - 1) → result; diff --git a/trainscript/main.cpp b/trainscript/main.cpp index badc2f6..9fa4f49 100644 --- a/trainscript/main.cpp +++ b/trainscript/main.cpp @@ -67,11 +67,7 @@ int main(int argc, char** argv) printf("Variable: %s : %s = ", var.first.c_str(), typeName(var.second->type.id)); - switch(var.second->type.id) { - case TypeID::Int: printf("%d", var.second->integer); break; - case TypeID::Real: printf("%f", var.second->real); break; - default: printf("???"); break; - } + var.second->printval(); printf("\n"); } diff --git a/trainscript/trainscript.l b/trainscript/trainscript.l index 8c25517..e25a676 100644 --- a/trainscript/trainscript.l +++ b/trainscript/trainscript.l @@ -11,8 +11,7 @@ %option yylineno %% \#[^\n]* ; // Eat all the comments! -[ ]+ ; // Eat all the whitespace! -\t { return TAB; } +[ \t]+ ; // Eat all the whitespace! \; { return SEMICOLON; } \: { return COLON; } \, { return COMMA; } @@ -26,16 +25,39 @@ \% { return MODULO; } \-\>|→ { return RARROW; } \<\-|← { return LARROW; } + +\< { return OP_LT; } +\<\= { return OP_LE; } +\> { return OP_GT; } +\>\= { return OP_GE; } +\= { return OP_EQ; } +\≠|\=\/\= { return OP_NEQ; } + VAR { return KW_VAR; } PUB { return KW_PUB; } PRI { return KW_PRI; } PTR { return KW_PTR; } + VOID { return KW_VOID; } INT { return KW_INT; } REAL { return KW_REAL; } TEXT { return KW_TEXT; } +BOOL { return KW_BOOL; } + +BEGIN { return KW_BEGIN; } +END { return KW_END; } +IF { return KW_IF; } +ELSE { return KW_ELSE; } +REPEAT { return KW_REPEAT; } +FROM { return KW_FROM; } +TO { return KW_TO; } +UNTIL { return KW_UNTIL; } +WHILE { return KW_WHILE; } [0-9]+\.[0-9]* { yylval->fval = atof(yytext); return REAL; } [0-9]+ { yylval->ival = atoi(yytext); return INT; } [a-zA-Z0-9']+ { yylval->text = strdup(yytext); return IDENTIFIER; } . { yyerror(NULL, "illegal token"); } %% +/* +\t { return TAB; } +*/ diff --git a/trainscript/trainscript.y b/trainscript/trainscript.y index cf9cca1..33b8570 100644 --- a/trainscript/trainscript.y +++ b/trainscript/trainscript.y @@ -15,6 +15,13 @@ namespace trainscript { Variable multiply(Variable lhs, Variable rhs); Variable divide(Variable lhs, Variable rhs); Variable modulo(Variable lhs, Variable rhs); + + Variable equals(Variable lhs, Variable rhs); + Variable inequals(Variable lhs, Variable rhs); + Variable less(Variable lhs, Variable rhs); + Variable lessEqual(Variable lhs, Variable rhs); + Variable greater(Variable lhs, Variable rhs); + Variable greaterEqual(Variable lhs, Variable rhs); } } @@ -65,6 +72,13 @@ void yyerror(void *scanner, const char *s); %token RARROW %token LARROW +%token OP_LT +%token OP_LE +%token OP_GT +%token OP_GE +%token OP_EQ +%token OP_NEQ + %token REAL %token INT %token IDENTIFIER @@ -73,17 +87,32 @@ void yyerror(void *scanner, const char *s); %token KW_PRI %token KW_VAR %token KW_PTR + %token KW_VOID %token KW_INT %token KW_REAL %token KW_TEXT +%token KW_BOOL + +%token KW_BEGIN +%token KW_END + +%token KW_IF +%token KW_ELSE +%token KW_REPEAT +%token KW_FROM +%token KW_TO +%token KW_UNTIL +%token KW_WHILE %type typeName %type variableDeclaration -%type indentation + +// %type indentation %type method %type methodDeclaration +%type block %type body %type argument @@ -101,6 +130,7 @@ void yyerror(void *scanner, const char *s); %start input %left PLUS MINUS MULTIPLY DIVIDE MODULO RARROW +%left OP_LT OP_LE OP_GT OP_GE OP_EQ OP_NEQ %% input: @@ -147,18 +177,22 @@ input: ; method: - methodDeclaration body { + methodDeclaration block { $$.header = $1; $$.body = $2; } ; +block: + KW_BEGIN body KW_END { $$ = $2; } +; + body: %empty { $$ = nullptr; } -| body indentation instruction SEMICOLON { +| body instruction SEMICOLON { auto *body = new MethodBody(); - body->indentation = $2; - body->instruction = $3; + body->indentation = 0; + body->instruction = $2; if($1 == nullptr) { $$ = body; } else { @@ -266,6 +300,12 @@ expression: | expression MULTIPLY expression { $$ = new ArithmeticExpression($1, $3); } | expression DIVIDE expression { $$ = new ArithmeticExpression($1, $3); } | expression MODULO expression { $$ = new ArithmeticExpression($1, $3); } +| expression OP_LT expression { $$ = new ArithmeticExpression($1, $3); } +| expression OP_LE expression { $$ = new ArithmeticExpression($1, $3); } +| expression OP_GT expression { $$ = new ArithmeticExpression($1, $3); } +| expression OP_GE expression { $$ = new ArithmeticExpression($1, $3); } +| expression OP_EQ expression { $$ = new ArithmeticExpression($1, $3); } +| expression OP_NEQ expression { $$ = new ArithmeticExpression($1, $3); } | expression RARROW IDENTIFIER { $$ = new VariableAssignmentExpression($3, $1); } ; @@ -302,13 +342,15 @@ typeName: | KW_INT { $$.id = TypeID::Int; $$.pointer = 0; } | KW_REAL { $$.id = TypeID::Real; $$.pointer = 0; } | KW_TEXT { $$.id = TypeID::Text; $$.pointer = 0; } +| KW_BOOL { $$.id = TypeID::Bool; $$.pointer = 0; } | KW_PTR LBRACKET typeName RBRACKET { $$ = $3; $$.pointer++; } ; - +/* indentation: TAB { $$ = 1; } | indentation TAB { $$ = $1 + 1; } ; +*/ %% @@ -317,5 +359,16 @@ indentation: #include "trainscript.l.h" void yyerror(void *scanner, const char *s) { - printf("Error: %s\n", s); + if(scanner == nullptr) { + printf("Error: %s\n", s); + return; + } + int line = 0; // yyget_lineno(scanner); + int col = 0; //yyget_column(scanner); + char *text = yyget_text(scanner); + printf( + "[%d:%d] Error: %s at '%s'\n", + line, col, + s, + text); } diff --git a/trainscript/tsvm.cpp b/trainscript/tsvm.cpp index 82ad58c..5b1ab12 100644 --- a/trainscript/tsvm.cpp +++ b/trainscript/tsvm.cpp @@ -17,12 +17,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 Variable Variable::Invalid = { Type::Invalid }; const Variable Variable::Void = { Type::Void }; const Variable Variable::Int = { Type::Int }; const Variable Variable::Real = { Type::Real }; const Variable Variable::Text = { Type::Text }; + const Variable Variable::Boolean = { Type::Boolean }; Module *VM::load(const void *buffer, size_t length) { @@ -160,5 +162,74 @@ namespace trainscript default: printf("modulo not supported for %s.\n", typeName(lhs.type.id)); return Variable::Invalid; } } + + Variable equals(Variable lhs, Variable rhs) + { + switch(lhs.type.id) { + case TypeID::Int: return mkbool(lhs.integer == rhs.integer); + case TypeID::Real: return mkbool(lhs.real == rhs.real); + case TypeID::Bool: return mkbool(lhs.boolean == rhs.boolean); + default: + printf("equals not supported for %s.\n", typeName(lhs.type.id)); + return Variable::Invalid; + } + } + + Variable inequals(Variable lhs, Variable rhs) + { + switch(lhs.type.id) { + case TypeID::Int: return mkbool(lhs.integer != rhs.integer); + case TypeID::Real: return mkbool(lhs.real != rhs.real); + case TypeID::Bool: return mkbool(lhs.boolean != rhs.boolean); + default: + printf("inequals not supported for %s.\n", typeName(lhs.type.id)); + return Variable::Invalid; + } + } + + + Variable less(Variable lhs, Variable rhs) + { + switch(lhs.type.id) { + case TypeID::Int: return mkbool(lhs.integer < rhs.integer); + case TypeID::Real: return mkbool(lhs.real < rhs.real); + default: + printf("equals not supported for %s.\n", typeName(lhs.type.id)); + return Variable::Invalid; + } + } + + Variable lessEqual(Variable lhs, Variable rhs) + { + switch(lhs.type.id) { + case TypeID::Int: return mkbool(lhs.integer <= rhs.integer); + case TypeID::Real: return mkbool(lhs.real <= rhs.real); + default: + printf("equals not supported for %s.\n", typeName(lhs.type.id)); + return Variable::Invalid; + } + } + + Variable greater(Variable lhs, Variable rhs) + { + switch(lhs.type.id) { + case TypeID::Int: return mkbool(lhs.integer > rhs.integer); + case TypeID::Real: return mkbool(lhs.real > rhs.real); + default: + printf("equals not supported for %s.\n", typeName(lhs.type.id)); + return Variable::Invalid; + } + } + + Variable greaterEqual(Variable lhs, Variable rhs) + { + switch(lhs.type.id) { + case TypeID::Int: return mkbool(lhs.integer >= rhs.integer); + case TypeID::Real: return mkbool(lhs.real >= rhs.real); + default: + printf("equals not supported for %s.\n", typeName(lhs.type.id)); + return Variable::Invalid; + } + } } } diff --git a/trainscript/tsvm.hpp b/trainscript/tsvm.hpp index bb12bac..b795f88 100644 --- a/trainscript/tsvm.hpp +++ b/trainscript/tsvm.hpp @@ -12,6 +12,7 @@ namespace trainscript using Int = int32_t; using Real = double; using Void = void; + using Bool = bool; extern bool verbose; @@ -55,6 +56,7 @@ namespace trainscript static const Type Int; static const Type Real; static const Type Text; + static const Type Boolean; }; struct Variable @@ -64,6 +66,7 @@ namespace trainscript Int integer; Real real; Text text; + Bool boolean; }; void printval() const @@ -71,6 +74,7 @@ namespace trainscript switch(this->type.id) { case TypeID::Int: printf("%d", this->integer); break; case TypeID::Real: printf("%f", this->real); break; + case TypeID::Bool: printf("%s", this->boolean ? "TRUE" : "FALSE"); break; default: printf("???"); break; } } @@ -80,6 +84,7 @@ namespace trainscript static const Variable Int; static const Variable Real; static const Variable Text; + static const Variable Boolean; }; static inline Variable mkvar(Int value) { @@ -94,6 +99,12 @@ namespace trainscript return v; } + static inline Variable mkbool(Bool value) { + Variable v = Variable::Boolean; + v.boolean = value; + return v; + } + class Module; class LocalContext : @@ -291,6 +302,7 @@ namespace trainscript 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: if(verbose) printf("assignment not supported.\n"); break; } diff --git a/trainscript/typeid.hpp b/trainscript/typeid.hpp index 9a5790f..57f48c2 100644 --- a/trainscript/typeid.hpp +++ b/trainscript/typeid.hpp @@ -9,6 +9,7 @@ namespace trainscript Int = 2, Real = 3, Text = 4, + Bool = 5, }; static const char *typeName(TypeID id) { @@ -18,6 +19,7 @@ namespace trainscript case TypeID::Int: return "INT"; case TypeID::Real: return "REAL"; case TypeID::Text: return "TEXT"; + case TypeID::Bool: return "BOOL"; default: return "unknown"; } }