old-trainOS/trainscript/trainscript.y
2015-08-13 20:12:09 +02:00

415 lines
9.7 KiB
Text

%{
#include <stdio.h>
#include <stdlib.h>
#include "common.h"
#include "tsvm.hpp"
using namespace trainscript;
namespace trainscript {
namespace ops {
Variable add(Variable lhs, Variable rhs);
Variable subtract(Variable lhs, Variable rhs);
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);
}
}
typedef union YYSTYPE YYSTYPE;
// stuff from flex that bison needs to know about:
int yylex (YYSTYPE * yylval_param , void *yyscanner);
void yyerror(void *scanner, const char *s);
#define scanner context->scanner
%}
%pure-parser
%lex-param {void * scanner}
%parse-param {ParserData * context}
%union {
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;
}
%token TAB
%token TYPENAME
%token SEMICOLON
%token COLON
%token COMMA
%token PIPE
%token PLUS
%token MINUS
%token MULTIPLY
%token DIVIDE
%token MODULO
%token LBRACKET
%token RBRACKET
%token RARROW
%token LARROW
%token OP_LT
%token OP_LE
%token OP_GT
%token OP_GE
%token OP_EQ
%token OP_NEQ
%token <fval> REAL
%token <ival> INT
%token <text> IDENTIFIER
%token KW_PUB
%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_THEN
%token KW_ELSE
%token KW_ELSEIF
%token KW_REPEAT
%token KW_FROM
%token KW_TO
%token KW_UNTIL
%token KW_WHILE
%token KW_DO
%type <type> typeName
%type <varDecl> variableDeclaration
// %type <indentation> indentation
%type <method> method
%type <methodHeader> methodDeclaration
%type <instruction> block
%type <body> body
%type <local> argument
%type <local> arguments
%type <local> argumentList
%type <local> methodLocals
%type <local> methodLocalList
%type <instruction> instruction
%type <instruction> expression
%type <instruction> condition
%type <instruction> elseIfLoop
%type <instruction> loop
%type <expressions> expressionList
%start input
%left PLUS MINUS MULTIPLY DIVIDE MODULO RARROW
%left OP_LT OP_LE OP_GT OP_GE OP_EQ OP_NEQ
%right KW_IF KW_THEN KW_ELSEIF KW_ELSE
%%
input:
%empty
| input variableDeclaration SEMICOLON {
auto *var = new Variable($2.variable);
context->module->variables.add( ker::String($2.name), var );
}
| input method {
using namespace trainscript;
auto *mod = context->module;
ScriptMethod *method = new ScriptMethod(mod, $2.body);
method->isPublic = $2.header.isPublic;
if($2.header.returnValue) {
method->returnValue = ker::Pair<ker::String, Variable>(
$2.header.returnValue->name,
$2.header.returnValue->variable);
}
LocalVariable *local = $2.header.locals;
while(local) {
method->locals.add( local->name, local->variable );
local = local->next;
}
LocalVariable *arg = $2.header.arguments;
while(arg) {
method->arguments.append( { arg->name, arg->variable } );
arg = arg->next;
}
context->module->methods.add( $2.header.name, method );
}
;
method:
methodDeclaration block {
$$.header = $1;
$$.body = $2;
}
;
block:
KW_BEGIN body KW_END {
auto *block = new Block();
MethodBody *mb = $2;
while(mb) {
if(mb->instruction != nullptr) {
block->instructions.append(mb->instruction);
}
mb = mb->next;
}
$$ = block;
}
;
body:
%empty { $$ = nullptr; }
| body instruction {
auto *body = new MethodBody();
body->indentation = 0;
body->instruction = $2;
if($1 == nullptr) {
$$ = body;
} else {
$$ = $1;
auto *it = $1;
while(it->next) { it = it->next; }
it->next = body;
}
}
;
methodDeclaration:
KW_PUB IDENTIFIER LBRACKET arguments RBRACKET methodLocals {
$$.isPublic = true;
$$.arguments = $4;
$$.locals = $6;
$$.returnValue = nullptr;
$$.name = $2;
}
| KW_PRI IDENTIFIER LBRACKET arguments RBRACKET methodLocals {
$$.isPublic = false;
$$.arguments = $4;
$$.locals = $6;
$$.returnValue = nullptr;
$$.name = $2;
}
| KW_PUB IDENTIFIER LBRACKET arguments RBRACKET RARROW argument methodLocals {
$$.isPublic = true;
$$.arguments = $4;
$$.locals = $8;
$$.returnValue = $7;
$$.name = $2;
}
| KW_PRI IDENTIFIER LBRACKET arguments RBRACKET RARROW argument methodLocals {
$$.isPublic = false;
$$.arguments = $4;
$$.locals = $8;
$$.returnValue = $7;
$$.name = $2;
}
;
methodLocals:
%empty { $$ = nullptr; }
| PIPE methodLocalList { $$ = $2; }
;
methodLocalList:
argument { $$ = $1; }
| methodLocalList COMMA argument {
auto *it = $1;
while(it->next) { it = it->next; }
it->next = $3;
$$ = $1;
}
;
arguments:
%empty { $$ = nullptr; }
| argumentList { $$ = $1; }
;
argumentList:
argument { $$ = $1; }
| argumentList COMMA argument {
auto *it = $1;
while(it->next) { it = it->next; }
it->next = $3;
$$ = $1;
}
;
argument:
IDENTIFIER COLON typeName {
$$ = new LocalVariable();
$$->name = $1;
$$->variable.type = $3;
$$->variable.integer = 0; // zero value
$$->next = nullptr;
}
;
instruction:
block { $$ = $1; }
| expression SEMICOLON { $$ = $1; }
| condition { $$ = $1; }
| loop { $$ = $1; }
;
loop:
KW_REPEAT instruction { $$ = new RepeatEndlessExpression($2); }
| KW_WHILE expression KW_DO instruction { $$ = new RepeatWhileExpression($2, $4); }
;
condition:
KW_IF expression KW_THEN instruction elseIfLoop { $$ = new IfExpression($2, $4, $5); }
;
elseIfLoop:
%empty { $$ = nullptr; }
| elseIfLoop KW_ELSEIF expression KW_THEN instruction {
if($$ == nullptr) {
$$ = new IfExpression($3, $5, nullptr);
} else {
IfExpression *exp = (IfExpression*)$1;
while(exp->blockFalse != nullptr) {
exp = (IfExpression*)exp->blockFalse;
}
exp->blockFalse = new IfExpression($3, $5, nullptr);
$$ = $1;
}
}
| elseIfLoop KW_ELSE instruction {
if($$ == nullptr) {
$$ = $3;
} else {
IfExpression *exp = (IfExpression*)$1;
while(exp->blockFalse != nullptr) {
exp = (IfExpression*)exp->blockFalse;
}
exp->blockFalse = $3;
$$ = $1;
}
}
;
expression:
INT { $$ = new ConstantExpression(mkvar($1)); }
| REAL { $$ = new ConstantExpression(mkvar($1)); }
// | TEXT { $$ = new ConstantExpression(mkvar($1)); }
| IDENTIFIER { $$ = new VariableExpression($1); }
| IDENTIFIER LBRACKET expressionList RBRACKET {
auto *call = new MethodInvokeExpression($1);
auto *list = $3;
while(list) {
call->parameters.append(list->instruction);
list = list->next;
}
$$ = call;
}
| LBRACKET expression RBRACKET { $$ = $2; }
| expression PLUS expression { $$ = new ArithmeticExpression<trainscript::ops::add>($1, $3); }
| expression MINUS expression { $$ = new ArithmeticExpression<trainscript::ops::subtract>($1, $3); }
| expression MULTIPLY expression { $$ = new ArithmeticExpression<trainscript::ops::multiply>($1, $3); }
| expression DIVIDE expression { $$ = new ArithmeticExpression<trainscript::ops::divide>($1, $3); }
| expression MODULO expression { $$ = new ArithmeticExpression<trainscript::ops::modulo>($1, $3); }
| expression OP_LT expression { $$ = new ArithmeticExpression<trainscript::ops::less>($1, $3); }
| expression OP_LE expression { $$ = new ArithmeticExpression<trainscript::ops::lessEqual>($1, $3); }
| expression OP_GT expression { $$ = new ArithmeticExpression<trainscript::ops::greater>($1, $3); }
| expression OP_GE expression { $$ = new ArithmeticExpression<trainscript::ops::greaterEqual>($1, $3); }
| expression OP_EQ expression { $$ = new ArithmeticExpression<trainscript::ops::equals>($1, $3); }
| expression OP_NEQ expression { $$ = new ArithmeticExpression<trainscript::ops::inequals>($1, $3); }
| expression RARROW IDENTIFIER { $$ = new VariableAssignmentExpression($3, $1); }
;
expressionList:
%empty { $$ = nullptr; }
| expression {
$$ = new ExpressionList();
$$->instruction = $1;
$$->next = nullptr;
}
| expressionList COMMA expression {
auto *list = new ExpressionList();
list->instruction = $3;
list->next = nullptr;
auto *it = $1;
while(it->next) { it = it->next; }
it->next = list;
$$ = $1;
}
;
variableDeclaration:
KW_VAR IDENTIFIER COLON typeName {
$$.name = $2;
$$.variable.type = $4;
$$.variable.integer = 0; // Initialize with zeroes
}
;
typeName:
KW_VOID { $$.id = TypeID::Void; $$.pointer = 0; }
| 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++; }
;
%%
#undef scanner
#include "trainscript.l.h"
void yyerror(void *scanner, const char *s) {
if(scanner == nullptr) {
kprintf("Error: %s\n", s);
return;
}
int line = 0; // yyget_lineno(scanner);
int col = 0; //yyget_column(scanner);
char *text = yyget_text(scanner);
kprintf(
"[%d:%d] Error: %s at '%s'\n",
line, col,
s,
text);
}