package net.phatcode.rel.main; /******************************************************************** * net.phatcode.rel.main.Parser.java * A handcrafted top down recursive descent parser * * Richard Eric Lope BSN RN * http://rel.phatcode.net * Started: March 06, 2016 * Ended: Ongoing * * License MIT: * Copyright (c) 2016 Richard Eric Lope * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in * the Software without restriction, including without limitation the rights to * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is furnished to do * so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. (As clarification, there is no * requirement that the copyright notice and permission be included in binary * distributions of the Software.) * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * * *******************************************************************/ import net.phatcode.rel.expressions.*; import net.phatcode.rel.multimedia.Graphics; import net.phatcode.rel.multimedia.Sonics; import net.phatcode.rel.parselets.Parselets; import net.phatcode.rel.parselets.Statement; import net.phatcode.rel.values.*; import org.lwjgl.input.Keyboard; import org.lwjgl.input.Mouse; import org.lwjgl.opengl.Display; import java.util.*; public class Parser { private List<Statement> statements = new ArrayList<>(); //AST private Map<String, Value> symbolTable = new HashMap<>(); // global variables private Map<String, Statement> userDefinedStructs = new HashMap<>(); // global user defined structs private Parselets parselets = new Parselets(); private Graphics graphics; private Sonics sonics; private List<Token> tokens; // tokens private int currentTokenIndex = 0; private int currentStatementIndex = 0; //error reporting private int errorTokenIndex = 0; //error reporting private int currentCodeLine = 0; //error reporting public Parser() { // Fetch this for our return statement symbolTable.put( "AnyaBASICfunctionReturn123456", new ValueNumber( 0 ) ); // Fetched for break statement symbolTable.put( "AnyaBASIConditonalBreak123456", new ValueInteger( 0 ) ); } public Parser( List<Token> tokens ) { super(); this.tokens = tokens; } private Token getToken( int offset ) { if( ( currentTokenIndex + offset ) >= tokens.size() ) { return new Token( Token.Type.UNKNOWN, "NO_TOKEN" ); } return tokens.get( currentTokenIndex + offset ); } // lookahead token public Token nextToken() { return getToken( 1 ); } public Token currentToken() { return getToken( 0 ); } private void eatToken( int offset ) { currentTokenIndex += offset; } public Token match( Token.Type type ) { Token token = currentToken(); if( token.getType() == type ) { eatToken( 1 ); } else { System.out.println( "Syntax ERROR: " + "Got '" + token.getText() + "'" + ". Expected: " + type + "\n@line: " + ( currentCodeLine + 1 ) ); errorTokenIndex = currentTokenIndex; printErrorLine(); System.exit( 1 ); } return token; } private Expression atom() { Expression result; if( currentToken().getType() == Token.Type.LEFT_PAREN ) { // handle parentheses match( Token.Type.LEFT_PAREN ); result = expression(); match( Token.Type.RIGHT_PAREN ); } else if( currentToken().getType() == Token.Type.NUMBER ) // is the expression a number? { result = new ValueNumber( Double.parseDouble( currentToken().getText() ) ); match( Token.Type.NUMBER ); } else if( currentToken().getType() == Token.Type.STRING ) // is it a string? { result = new ValueString( currentToken().getTextOriginal() ); match( Token.Type.STRING ); } else //variables and functions { // if not a left paren ( if( nextToken().getType() != Token.Type.LEFT_PAREN ) { // not a left bracket [ either so it's a named variable or a struct if( nextToken().getType() != Token.Type.LEFT_BRACKET ) { if( nextToken().getType() != Token.Type.DOT ) { if( nextToken().getType() != Token.Type.LEFT_BRACE ) { result = new ExpressionVariable( currentToken().getText(), this ); match( Token.Type.KEYWORD ); } else // x = map{key} { String mapName = currentToken().getText(); match( Token.Type.KEYWORD ); match( Token.Type.LEFT_BRACE ); Expression key = arithmeticExpression(); match( Token.Type.RIGHT_BRACE ); result = new ExpressionMapVariable( mapName, key, this ); } } else //struct field expression { String varname = currentToken().getText(); match( Token.Type.KEYWORD ); match( Token.Type.DOT ); String fieldname = currentToken().getText(); result = new ExpressionStructFieldVariable( varname, fieldname, this ); match( Token.Type.KEYWORD ); } } else //it's a bracket so it's an array access. ie. a = arrayKho[bhe] { String name = currentToken().getText(); match( Token.Type.KEYWORD ); match( Token.Type.LEFT_BRACKET ); Expression index = expression(); match( Token.Type.RIGHT_BRACKET ); // check for dot // if dot then it's an struct array acccess // print(a[0].z) if( currentToken().getType() == Token.Type.DOT ) { match( Token.Type.DOT ); result = new ExpressionArrayStructFieldVariable( name, index, currentToken().getText(), this ); match( Token.Type.KEYWORD ); } else if( currentToken().getType() == Token.Type.LEFT_BRACKET ) { // 2D array match( Token.Type.LEFT_BRACKET ); Expression column = expression(); match( Token.Type.RIGHT_BRACKET ); if( currentToken().getType() != Token.Type.DOT ) { result = new ExpressionArray2dVariable( name, index, column, this ); } else { match( Token.Type.DOT ); String fieldname = currentToken().getText(); result = new ExpressionArray2dStructFieldVariable( name, index, column, fieldname, this ); match( Token.Type.KEYWORD ); } } else // normal array access. print(a[0]) { result = new ExpressionArrayVariable( name, index, this ); } } } else // Function name is undefined at call { result = parselets.expressionFunction( this ); } } return result; // get value } private Expression signedAtom() { if( currentToken().getType() == Token.Type.MINUS ) { match( Token.Type.MINUS ); return new ExpressionSignedAtom( atom() ); } if( currentToken().getType() == Token.Type.PLUS ) { match( Token.Type.PLUS ); } return atom(); } // Base ^ exponent private Expression factor() { Expression result = signedAtom(); while( currentToken().getType() == Token.Type.CARET ) { switch( currentToken().getType() ) { case CARET: match( Token.Type.CARET ); result = new ExpressionOperator( result, Token.Type.CARET, atom() ); break; default: break; } } return result; } private Expression term() { Expression result = factor(); while( isMulOp( currentToken().getType() ) ) { switch( currentToken().getType() ) { case ASTERISK: match( Token.Type.ASTERISK ); result = new ExpressionOperator( result, Token.Type.ASTERISK, factor() ); break; case SLASH: match( Token.Type.SLASH ); result = new ExpressionOperator( result, Token.Type.SLASH, factor() ); break; case PERCENT: match( Token.Type.PERCENT ); result = new ExpressionOperator( result, Token.Type.PERCENT, factor() ); break; default: break; } } return result; } public Expression arithmeticExpression() { Expression result = term(); while( isAddOp( currentToken().getType() ) ) { switch( currentToken().getType() ) { case PLUS: match( Token.Type.PLUS ); result = new ExpressionOperator( result, Token.Type.PLUS, term() ); break; case MINUS: match( Token.Type.MINUS ); result = new ExpressionOperator( result, Token.Type.MINUS, term() ); break; default: break; } } return result; } // Used for conditions in while loops, if then else constructs, etc. private Expression relationalExpression() { Expression result = arithmeticExpression(); if( isRelationalOp( currentToken().getType() ) ) { switch( currentToken().getType() ) { case EQUAL: match( Token.Type.EQUAL ); result = new ExpressionOperator( result, Token.Type.EQUAL, arithmeticExpression() ); break; case NOTEQUAL: match( Token.Type.NOTEQUAL ); result = new ExpressionOperator( result, Token.Type.NOTEQUAL, arithmeticExpression() ); break; case LESS: match( Token.Type.LESS ); result = new ExpressionOperator( result, Token.Type.LESS, arithmeticExpression() ); break; case GREATER: match( Token.Type.GREATER ); result = new ExpressionOperator( result, Token.Type.GREATER, arithmeticExpression() ); break; case LESSEQUAL: match( Token.Type.LESSEQUAL ); result = new ExpressionOperator( result, Token.Type.LESSEQUAL, arithmeticExpression() ); break; case GREATEREQUAL: match( Token.Type.GREATEREQUAL ); result = new ExpressionOperator( result, Token.Type.GREATEREQUAL, arithmeticExpression() ); break; default: break; } } return result; } private Expression booleanFactor() { return relationalExpression(); } private Expression booleanNotFactor() { if( currentToken().getType() == Token.Type.NOT ) { match( Token.Type.NOT ); return new ExpressionNotOperator( booleanFactor() ); } return booleanFactor(); } private Expression booleanTerm() { Expression result = booleanNotFactor(); while( currentToken().getType() == Token.Type.AND ) { match( Token.Type.AND ); result = new ExpressionOperator( result, Token.Type.AND, booleanNotFactor() ); } return result; } private Expression booleanExpression() { Expression result = booleanTerm(); while( currentToken().getType() == Token.Type.OR ) { match( Token.Type.OR ); result = new ExpressionOperator( result, Token.Type.OR, booleanTerm() ); } return result; } public Expression expression() { return booleanExpression(); } public void setGraphicsMode( List<Expression> params ) { if( params.size() < 1 ) { graphics = new Graphics(); } else if( params.size() == 2 ) { graphics = new Graphics( params.get( 0 ).evaluate().toInteger(), params.get( 1 ).evaluate().toInteger(), 0 ); } else { graphics = new Graphics( params.get( 0 ).evaluate().toInteger(), params.get( 1 ).evaluate().toInteger(), params.get( 2 ).evaluate().toInteger() ); } } public void screenClear( List<Expression> params ) { if( params.size() < 1 ) { graphics.cls(); } else { graphics.cls(); graphics.drawBoxFilled( 0, 0, graphics.getScreenWidth(), graphics.getScreenHeight(), (float) params.get( 0 ).evaluate().toNumber(), (float) params.get( 1 ).evaluate().toNumber(), (float) params.get( 2 ).evaluate().toNumber(), (float) params.get( 3 ).evaluate().toNumber() ); } } public void setColor( List<Expression> params ) { float r = (float) params.get( 0 ).evaluate().toNumber(); float g = (float) params.get( 1 ).evaluate().toNumber(); float b = (float) params.get( 2 ).evaluate().toNumber(); float a = (float) params.get( 3 ).evaluate().toNumber(); graphics.setColor( r, g, b, a ); } public void setBlendMode( Expression mode ) { graphics.setBlendMode( mode.evaluate().toInteger() ); } public void drawLine( List<Expression> params ) { int x1 = (int) params.get( 0 ).evaluate().toNumber(); int y1 = (int) params.get( 1 ).evaluate().toNumber(); int x2 = (int) params.get( 2 ).evaluate().toNumber(); int y2 = (int) params.get( 3 ).evaluate().toNumber(); if( params.size() > 4 ) { float r = (float) params.get( 4 ).evaluate().toNumber(); float g = (float) params.get( 5 ).evaluate().toNumber(); float b = (float) params.get( 6 ).evaluate().toNumber(); float a = (float) params.get( 7 ).evaluate().toNumber(); graphics.drawLine( x1, y1, x2, y2, r, g, b, a ); } else { graphics.drawLine( x1, y1, x2, y2 ); } } public void drawRect( List<Expression> params ) { int x1 = (int) params.get( 0 ).evaluate().toNumber(); int y1 = (int) params.get( 1 ).evaluate().toNumber(); int x2 = (int) params.get( 2 ).evaluate().toNumber(); int y2 = (int) params.get( 3 ).evaluate().toNumber(); if( params.size() > 4 ) { float r = (float) params.get( 4 ).evaluate().toNumber(); float g = (float) params.get( 5 ).evaluate().toNumber(); float b = (float) params.get( 6 ).evaluate().toNumber(); float a = (float) params.get( 7 ).evaluate().toNumber(); graphics.drawBox( x1, y1, x2, y2, r, g, b, a ); } else { graphics.drawBox( x1, y1, x2, y2 ); } } public void drawRectFilled( List<Expression> params ) { int x1 = (int) params.get( 0 ).evaluate().toNumber(); int y1 = (int) params.get( 1 ).evaluate().toNumber(); int x2 = (int) params.get( 2 ).evaluate().toNumber(); int y2 = (int) params.get( 3 ).evaluate().toNumber(); if( params.size() > 4 ) { float r = (float) params.get( 4 ).evaluate().toNumber(); float g = (float) params.get( 5 ).evaluate().toNumber(); float b = (float) params.get( 6 ).evaluate().toNumber(); float a = (float) params.get( 7 ).evaluate().toNumber(); graphics.drawBoxFilled( x1, y1, x2, y2, r, g, b, a ); } else { graphics.drawBoxFilled( x1, y1, x2, y2 ); } } //public void drawEllipse( int x, int y, int a, int b, int degrees, // float red, float green, float blue, float alpha ) public void drawEllipse( List<Expression> params ) { int x = (int) params.get( 0 ).evaluate().toNumber(); int y = (int) params.get( 1 ).evaluate().toNumber(); int a = (int) params.get( 2 ).evaluate().toNumber(); int b = (int) params.get( 3 ).evaluate().toNumber(); int degrees = (int) params.get( 4 ).evaluate().toNumber(); if( params.size() > 5 ) { float red = (float) params.get( 5 ).evaluate().toNumber(); float green = (float) params.get( 6 ).evaluate().toNumber(); float blue = (float) params.get( 7 ).evaluate().toNumber(); float alpha = (float) params.get( 8 ).evaluate().toNumber(); graphics.drawEllipse( x, y, a, b, degrees, red, green, blue, alpha ); } else { graphics.drawEllipse( x, y, a, b, degrees ); } } public void drawEllipseFilled( List<Expression> params ) { int x = (int) params.get( 0 ).evaluate().toNumber(); int y = (int) params.get( 1 ).evaluate().toNumber(); int a = (int) params.get( 2 ).evaluate().toNumber(); int b = (int) params.get( 3 ).evaluate().toNumber(); int degrees = (int) params.get( 4 ).evaluate().toNumber(); if( params.size() > 5 ) { float red = (float) params.get( 5 ).evaluate().toNumber(); float green = (float) params.get( 6 ).evaluate().toNumber(); float blue = (float) params.get( 7 ).evaluate().toNumber(); float alpha = (float) params.get( 8 ).evaluate().toNumber(); graphics.drawEllipseFilled( x, y, a, b, degrees, red, green, blue, alpha ); } else { graphics.drawEllipseFilled( x, y, a, b, degrees ); } } public void drawString( List<Expression> params ) { int x = (int) params.get( 0 ).evaluate().toNumber(); int y = (int) params.get( 1 ).evaluate().toNumber(); if( params.size() > 3 ) { float red = (float) params.get( 2 ).evaluate().toNumber(); float green = (float) params.get( 3 ).evaluate().toNumber(); float blue = (float) params.get( 4 ).evaluate().toNumber(); float alpha = (float) params.get( 5 ).evaluate().toNumber(); String text = params.get( 6 ).evaluate().toString(); graphics.drawString( x, y, red, green, blue, alpha, text ); } else { String text = params.get( 2 ).evaluate().toString(); graphics.drawString( x, y, text ); } } //public void drawSprite( float x, float y, // int flipmode, SpriteGL sprite ) //public void drawSprite( float x, float y, // float r, float g, float b, // int flipmode, SpriteGL sprite ) // spriteGL is an index fetched from multimedia public void drawImage( List<Expression> params ) { int x = (int) params.get( 0 ).evaluate().toNumber(); int y = (int) params.get( 1 ).evaluate().toNumber(); if( params.size() == 4 ) { int flip = (int) params.get( 2 ).evaluate().toNumber(); int index = (int) params.get( 3 ).evaluate().toNumber(); graphics.drawSprite( x, y, flip, index ); } else { float red = (float) params.get( 2 ).evaluate().toNumber(); float green = (float) params.get( 3 ).evaluate().toNumber(); float blue = (float) params.get( 4 ).evaluate().toNumber(); float alpha = (float) params.get( 5 ).evaluate().toNumber(); int flip = (int) params.get( 6 ).evaluate().toNumber(); int index = (int) params.get( 7 ).evaluate().toNumber(); graphics.drawSprite( x, y, red, green, blue, alpha, flip, index ); } } //public void drawTransformedSprite( float x, float y, // float rotation, // float scaleX, float scaleY, // int flipmode, SpriteGL sprite ) //public void drawTransformedSprite( float x, float y, // float rotation, // float scaleX, float scaleY, // float r, float g, float b, float a, // int flipmode, SpriteGL sprite ) // spriteGL is an index fetched from multimedia public void drawTransformedImage( List<Expression> params ) { int x = (int) params.get( 0 ).evaluate().toNumber(); int y = (int) params.get( 1 ).evaluate().toNumber(); float angle = (int) params.get( 2 ).evaluate().toNumber(); float scaleX = (float) params.get( 3 ).evaluate().toNumber(); float scaleY = (float) params.get( 4 ).evaluate().toNumber(); if( params.size() == 7 ) { int flip = (int) params.get( 5 ).evaluate().toNumber(); int index = (int) params.get( 6 ).evaluate().toNumber(); graphics.drawTransformedSprite( x, y, angle, scaleX, scaleY, flip, index ); } else { float red = (float) params.get( 5 ).evaluate().toNumber(); float green = (float) params.get( 6 ).evaluate().toNumber(); float blue = (float) params.get( 7 ).evaluate().toNumber(); float alpha = (float) params.get( 8 ).evaluate().toNumber(); int flip = (int) params.get( 9 ).evaluate().toNumber(); int index = (int) params.get( 10 ).evaluate().toNumber(); graphics.drawTransformedSprite( x, y, angle, scaleX, scaleY, red, green, blue, alpha, flip, index ); } } // DrawScrolledImage(x,y,scalex,scaley,u,v[,r,g,b,a]),flipmode,arrayIndex public void drawScrolledImage( List<Expression> params ) { float x = (float) params.get( 0 ).evaluate().toNumber(); float y = (float) params.get( 1 ).evaluate().toNumber(); float sx = (float) params.get( 2 ).evaluate().toNumber(); float sy = (float) params.get( 3 ).evaluate().toNumber(); float u = (float) params.get( 4 ).evaluate().toNumber(); float v = (float) params.get( 5 ).evaluate().toNumber(); if( params.size() == 8 ) { int flipmode = params.get( 6 ).evaluate().toInteger(); int index = params.get( 7 ).evaluate().toInteger(); graphics.drawScrolledSprite( x, y, sx, sy, u, v, flipmode, index ); } else { float red = (float) params.get( 6 ).evaluate().toNumber(); float green = (float) params.get( 7 ).evaluate().toNumber(); float blue = (float) params.get( 8 ).evaluate().toNumber(); float alpha = (float) params.get( 9 ).evaluate().toNumber(); int flipmode = params.get( 10 ).evaluate().toInteger(); int index = params.get( 11 ).evaluate().toInteger(); graphics.drawScrolledSprite( x, y, sx, sy, u, v, red, green, blue, alpha, flipmode, index ); } } // DrawFancyLine(x1, y1, x2, y2, width, type ) // DrawFancyLine(x1, y1, x2, y2, width, type, r, g, b, a ) public void drawFancyLine( List<Expression> params ) { int x1 = params.get( 0 ).evaluate().toInteger(); int y1 = params.get( 1 ).evaluate().toInteger(); int x2 = params.get( 2 ).evaluate().toInteger(); int y2 = params.get( 3 ).evaluate().toInteger(); int width = params.get( 4 ).evaluate().toInteger(); int type = params.get( 5 ).evaluate().toInteger(); if( params.size() == 6 ) { graphics.drawSpriteOnLine( x1, y1, x2, y2, width, type ); } else { float red = (float) params.get( 6 ).evaluate().toNumber(); float green = (float) params.get( 7 ).evaluate().toNumber(); float blue = (float) params.get( 8 ).evaluate().toNumber(); float alpha = (float) params.get( 9 ).evaluate().toNumber(); graphics.drawSpriteOnLine( x1, y1, x2, y2, width, type, red, green, blue, alpha ); } } // loadimage(String a, String array, index ) // a = filename // array = name of array // index = index on the array where to load the image public void loadImage( List<Expression> params ) { String fileName = params.get( 0 ).evaluate().toString(); String arrayName = params.get( 1 ).evaluate().toString(); int imageIndex = graphics.loadImage( fileName ); assignArrayVariable( arrayName, params.get( 2 ).evaluate(), new ValueNumber( imageIndex ) ); } //public void getSubImage( float x1, float y1, float x2, float y2, // int source, dest ) // spriteGL is an index fetched from multimedia //"getsubimage(0,0, 200, 200,imageArray[0]),imageArray,2", public void getSubImage( List<Expression> params ) { int x1 = (int) params.get( 0 ).evaluate().toNumber(); int y1 = (int) params.get( 1 ).evaluate().toNumber(); int x2 = (int) params.get( 2 ).evaluate().toNumber(); int y2 = (int) params.get( 3 ).evaluate().toNumber(); int indexSource = (int) params.get( 4 ).evaluate().toNumber(); String arrayName = params.get( 5 ).evaluate().toString(); int imageIndex = graphics.getSprites().size(); assignArrayVariable( arrayName, params.get( 6 ).evaluate(), new ValueNumber( imageIndex ) ); graphics.getSprite( x1, y1, x2, y2, indexSource, imageIndex ); } public void executeSoundInit() { sonics = new Sonics(); } public void executeSoundLoad( List<Expression> params ) { String name = params.get( 0 ).evaluate().toString(); boolean isLooping = params.get( 1 ).evaluate().toBoolean(); sonics.addSample( name, isLooping ); } public void executeSoundPlay( String name ) { sonics.play( name ); } public void executeSoundPause( String name ) { sonics.pause( name ); } public void executeSoundStop( String name ) { sonics.stop( name ); } public void exit( int value ) { if( (graphics == null) && (sonics == null) ) { System.exit( value ); } else { if( graphics != null ) graphics.destroy(); if( sonics != null ) sonics.shutDown(); System.exit( value ); } } private Expression keyDown( int param ) { int res = 0; if( Keyboard.isKeyDown( param ) ) { res = 1; } return new ValueNumber( res ); } private Expression boxIntersects( List<Expression> args ) { float ax1 = (float) args.get( 0 ).evaluate().toNumber(); float ay1 = (float) args.get( 1 ).evaluate().toNumber(); float ax2 = (float) args.get( 2 ).evaluate().toNumber(); float ay2 = (float) args.get( 3 ).evaluate().toNumber(); float bx1 = (float) args.get( 4 ).evaluate().toNumber(); float by1 = (float) args.get( 5 ).evaluate().toNumber(); float bx2 = (float) args.get( 6 ).evaluate().toNumber(); float by2 = (float) args.get( 7 ).evaluate().toNumber(); int res = Utils.boxIntersects( ax1, ay1, ax2, ay2, bx1, by1, bx2, by2 ); return new ValueInteger( res ); } private Expression circleIntersects( List<Expression> args ) { float ax = (float) args.get( 0 ).evaluate().toNumber(); float ay = (float) args.get( 1 ).evaluate().toNumber(); float ar = (float) args.get( 2 ).evaluate().toNumber(); float bx = (float) args.get( 3 ).evaluate().toNumber(); float by = (float) args.get( 4 ).evaluate().toNumber(); float br = (float) args.get( 5 ).evaluate().toNumber(); int res = Utils.circleIntersects( ax, ay, ar, bx, by, br ); return new ValueInteger( res ); } private Expression subString( List<Expression> args ) { String name = args.get( 0 ).evaluate().toString(); int begin = args.get( 1 ).evaluate().toInteger(); if( args.size() == 2 ) { return new ValueString( name.substring( begin ) ); } else { int end = args.get( 2 ).evaluate().toInteger(); return new ValueString( name.substring( begin, end ) ); } } private Expression splitString( List<Expression> args ) { String name = args.get( 0 ).evaluate().toString(); String regex = args.get( 1 ).evaluate().toString(); String[] array = name.split( regex ); List<Expression> elements = new ArrayList<>( ); for( int i = 0; i < array.length; i++ ) { elements.add( new ValueString(array[ i ]) ); } return new ValueArray( name, elements ); } private Expression windowClosed() { int res = 0; if( Display.isCloseRequested() ) { res = 1; } return new ValueNumber( res ); } // // identify keyword public Statement identifyKeyword( Token.Type type, List<Statement> statementAST ) { Statement result = null; switch( type ) { case PRINT: result = parselets.statementPrint( statementAST, this ); currentCodeLine++; currentStatementIndex = currentTokenIndex; break; case WRITE: result = parselets.statementWrite( statementAST, this ); currentCodeLine++; currentStatementIndex = currentTokenIndex; break; case DELAY: result = parselets.statementDelay( statementAST, this ); currentCodeLine++; currentStatementIndex = currentTokenIndex; break; case INPUT: result = parselets.statementInput( statementAST, this ); currentCodeLine++; currentStatementIndex = currentTokenIndex; break; case WHILE: result = parselets.statementWhile( statementAST, this ); currentCodeLine++; currentStatementIndex = currentTokenIndex; break; case IF: result = parselets.statementIf( statementAST, this ); currentCodeLine++; currentStatementIndex = currentTokenIndex; break; case FOR: result = parselets.statementFor( statementAST, this ); currentCodeLine++; currentStatementIndex = currentTokenIndex; break; case REPEAT: result = parselets.statementRepeat( statementAST, this ); currentCodeLine++; currentStatementIndex = currentTokenIndex; break; case FREE: result = parselets.statementFree( statementAST, this ); currentCodeLine++; currentStatementIndex = currentTokenIndex; break; case FUNCTION: // function definition result = parselets.statementFunctionDefinition( statementAST, this ); currentCodeLine++; currentStatementIndex = currentTokenIndex; break; case TYPE: // function definition result = parselets.statementStruct( statementAST, this ); currentCodeLine++; currentStatementIndex = currentTokenIndex; break; case RETURN: // function definition result = parselets.statementReturn( statementAST, this ); currentCodeLine++; currentStatementIndex = currentTokenIndex; break; case SCREEN: // function definition result = parselets.statementScreen( statementAST, this ); currentCodeLine++; currentStatementIndex = currentTokenIndex; break; case SCREEN_CLEAR: // function definition result = parselets.statementScreenClear( statementAST, this ); currentCodeLine++; currentStatementIndex = currentTokenIndex; break; case SCREEN_FLIP: // function definition result = parselets.statementScreenFlip( statementAST, this ); currentCodeLine++; currentStatementIndex = currentTokenIndex; break; case DRAW_LINE: // function definition result = parselets.statementDrawLine( statementAST, this ); currentCodeLine++; currentStatementIndex = currentTokenIndex; break; case DRAW_ELLIPSE: // function definition result = parselets.statementDrawEllipse( statementAST, this ); currentCodeLine++; currentStatementIndex = currentTokenIndex; break; case DRAW_STRING: // function definition result = parselets.statementDrawString( statementAST, this ); currentCodeLine++; currentStatementIndex = currentTokenIndex; break; case SET_COLOR: // function definition result = parselets.statementSetColor( statementAST, this ); currentCodeLine++; currentStatementIndex = currentTokenIndex; break; case SET_BLENDMODE: // function definition result = parselets.statementSetBlendMode( statementAST, this ); currentCodeLine++; currentStatementIndex = currentTokenIndex; break; case LOAD_IMAGE: // function definition result = parselets.statementLoadImage( statementAST, this ); currentCodeLine++; currentStatementIndex = currentTokenIndex; break; case DRAW_IMAGE: // function definition result = parselets.statementDrawImage( statementAST, this ); currentCodeLine++; currentStatementIndex = currentTokenIndex; break; case DRAW_FANCY_LINE: // function definition result = parselets.statementDrawFancyLine( statementAST, this ); currentCodeLine++; currentStatementIndex = currentTokenIndex; break; case DRAW_TRANSFORMED_IMAGE: // function definition result = parselets.statementDrawTransformedImage( statementAST, this ); currentCodeLine++; currentStatementIndex = currentTokenIndex; break; case DRAW_SCROLLED_IMAGE: // function definition result = parselets.statementDrawScrolledImage( statementAST, this ); currentCodeLine++; currentStatementIndex = currentTokenIndex; break; case GET_SUB_IMAGE: // function definition result = parselets.statementGetSubImage( statementAST, this ); currentCodeLine++; currentStatementIndex = currentTokenIndex; break; case SOUND_INIT: // function definition result = parselets.statementSoundInit( statementAST, this ); currentCodeLine++; currentStatementIndex = currentTokenIndex; break; case SOUND_LOAD: // function definition result = parselets.statementSoundLoad( statementAST, this ); currentCodeLine++; currentStatementIndex = currentTokenIndex; break; case SOUND_PLAY: result = parselets.statementSoundPlay( statementAST, this ); currentCodeLine++; currentStatementIndex = currentTokenIndex; break; case SOUND_PAUSE: result = parselets.statementSoundPause( statementAST, this ); currentCodeLine++; currentStatementIndex = currentTokenIndex; break; case SOUND_STOP: result = parselets.statementSoundStop( statementAST, this ); currentCodeLine++; currentStatementIndex = currentTokenIndex; break; case SWAP: // swap result = parselets.statementSwap( statementAST, this ); currentCodeLine++; currentStatementIndex = currentTokenIndex; break; case BREAK: // swap result = parselets.statementBreak( statementAST, this ); currentCodeLine++; currentStatementIndex = currentTokenIndex; break; case EXIT: // function definition result = parselets.statementExit( statementAST, this ); currentCodeLine++; currentStatementIndex = currentTokenIndex; break; case VAR: // var definition if( getToken( 2 ).getType() == Token.Type.LEFT_BRACKET ) { // var vector2d[] = {...} result = parselets.statementArrayDefinition( statementAST, this ); } else if( getToken( 2 ).getType() == Token.Type.LEFT_BRACE ) { // var map{:} result = parselets.statementMapDefinition( statementAST, this ); } else { result = parselets.statementVarDefinition( statementAST, this ); } currentCodeLine++; currentStatementIndex = currentTokenIndex; break; case KEYWORD: if( nextToken().getType() == Token.Type.ASSIGNMENT ) { result = parselets.statementAssign( statementAST, this ); currentCodeLine++; currentStatementIndex = currentTokenIndex; } else if( nextToken().getType() == Token.Type.LEFT_BRACKET ) { result = parselets.statementArrayAssignment( statementAST, this ); currentCodeLine++; currentStatementIndex = currentTokenIndex; } else if( nextToken().getType() == Token.Type.DOT ) // Struct { result = parselets.statementStructFieldAssign( statementAST, this ); currentCodeLine++; currentStatementIndex = currentTokenIndex; } else if( nextToken().getType() == Token.Type.LEFT_BRACE ) { result = parselets.statementMapAssignment( statementAST, this ); currentCodeLine++; currentStatementIndex = currentTokenIndex; } else if( userDefinedStructs.containsKey( currentToken().getText() ) ) { // user defined struct // Check two tokens forward if it's "[" if( getToken( 2 ).getType() != Token.Type.LEFT_BRACKET ) { result = parselets.statementStructInstantiation( statementAST, this ); } else // Array of Structs. Vector2D a[100] { result = parselets.statementArrayStructInitialization( statementAST, this ); } currentCodeLine++; currentStatementIndex = currentTokenIndex; } else //function call as a statement { result = parselets.statementFunctionCall( statementAST, this ); currentCodeLine++; currentStatementIndex = currentTokenIndex; } break; default: System.out.println( "ERROR! Undefined Keyword!: " + type.toString() ); exit( 1 ); break; } return result; } // TODO: 4/7/2016 // make Arg a List so that we can support multiple parameters public Expression executeFunction( String funky, List<Expression> args ) { Expression res = null; switch( funky ) { case "sin": res = new ValueNumber( Math.sin( args.get( 0 ).evaluate().toNumber() ) ); break; case "cos": res = new ValueNumber( Math.cos( args.get( 0 ).evaluate().toNumber() ) ); break; case "tan": res = new ValueNumber( Math.tan( args.get( 0 ).evaluate().toNumber() ) ); break; case "asin": res = new ValueNumber( Math.asin( args.get( 0 ).evaluate().toNumber() ) ); break; case "acos": res = new ValueNumber( Math.acos( args.get( 0 ).evaluate().toNumber() ) ); break; case "atan": res = new ValueNumber( Math.atan( args.get( 0 ).evaluate().toNumber() ) ); break; case "atan2": res = new ValueNumber( Math.atan2( args.get( 0 ).evaluate().toNumber(), args.get( 1 ).evaluate().toNumber() ) ); break; case "ceil": res = new ValueNumber( Math.ceil( args.get( 0 ).evaluate().toNumber() ) ); break; case "floor": res = new ValueNumber( Math.floor( args.get( 0 ).evaluate().toNumber() ) ); break; case "round": res = new ValueNumber( Math.round( args.get( 0 ).evaluate().toNumber() ) ); break; case "exp": res = new ValueNumber( Math.exp( args.get( 0 ).evaluate().toNumber() ) ); break; case "log": res = new ValueNumber( Math.log( args.get( 0 ).evaluate().toNumber() ) ); break; case "random": res = new ValueNumber( Math.random() * args.get( 0 ).evaluate().toNumber() ); break; case "abs": res = new ValueNumber( Math.abs( args.get( 0 ).evaluate().toNumber() ) ); break; case "sqrt": res = new ValueNumber( Math.sqrt( args.get( 0 ).evaluate().toNumber() ) ); break; case "keydown": res = keyDown( (int) args.get( 0 ).evaluate().toNumber() ); break; case "timer": res = new ValueNumber( System.nanoTime() / 1000000000.0 ); break; case "format": res = new ValueString( String.format( args.get( 0 ).evaluate().toString(), args.get( 1 ).evaluate().toNumber() ) ); break; case "int": res = new ValueInteger( args.get( 0 ).evaluate().toInteger() ); break; case "boxintersects": res = boxIntersects( args ); break; case "circleintersects": res = circleIntersects( args ); break; case "sizeof": if( args.size() == 1 ) { if( args.get( 0 ).evaluate() instanceof ValueArray ) res = new ValueInteger( args.get( 0 ).evaluate().toArray().size() ); else res = new ValueInteger( ((ValueMap)args.get( 0 ).evaluate()).toMap().size() ); } else { if( args.get(1).evaluate().toInteger() == 0 ) res = new ValueInteger( ( (ValueArray) args.get( 0 ).evaluate() ).getRows() ); else res = new ValueInteger( ( (ValueArray) args.get( 0 ).evaluate() ).getColumns() ); } break; case "containskey": // containsKye(mapName, keyName) boolean val = ((ValueMap)args.get( 0 ).evaluate()).toMap().containsKey( args.get( 1 ).evaluate().toString() ); res = new ValueInteger( val ? 1 : 0 ); break; case "substring": res = subString( args ); break; case "stringlength": res = new ValueInteger( args.get( 0 ).evaluate().toString().length() ); break; case "valascii": res = new ValueInteger( args.get( 0 ).evaluate().toString().charAt( args.get( 1 ).evaluate().toInteger() ) ); break; case "val": res = new ValueNumber( Double.parseDouble( args.get( 0 ).evaluate().toString() ) ); break; case "isdigit": res = new ValueInteger( Character.isDigit( args.get( 0 ).evaluate().toString().charAt(0) ) ? 1 : 0 ); break; case "isletter": res = new ValueInteger( Character.isLetter( args.get( 0 ).evaluate().toString().charAt(0) ) ? 1 : 0 ); break; case "lowercase": res = new ValueString( args.get( 0 ).evaluate().toString().toLowerCase() ); break; case "uppercase": res = new ValueString( args.get( 0 ).evaluate().toString().toUpperCase() ); break; case "split": res = splitString(args); break; case "mousex": res = new ValueNumber( Mouse.getX() ); break; case "mousey": res = new ValueNumber( graphics.getScreenHeight() - Mouse.getY() ); break; case "mousebuttondown": res = new ValueInteger( Mouse.isButtonDown(args.get(0).evaluate().toInteger()) ? 1 : 0 ); break; case "windowclosed": res = windowClosed(); break; default: // user function call as a part of a expression;ie. print(a+ foo() +meh()) if( symbolTable.containsKey( funky ) ) { res = executeUserFunction( funky, args ); } else { System.out.println( "Runtime ERROR: Function '" + funky + "' undefined." ); exit( 1 ); } break; } return res; } // Executes user function as an Expression and not as a Statement // Used for things like a = foo(); private Expression executeUserFunction( String funky, List<Expression> args ) { // Get block statements from function list // assign variables from function variablenames // so that arguments are assigned to function parameters if( !symbolTable.containsKey( funky ) ) { System.out.println( "Runtime ERROR: Function '" + funky + "' undefined." ); exit( 1 ); } List<String> params = ( (ValueFunction) symbolTable.get( funky ) ).getVariables(); if( params.size() != args.size() ) { System.out.println( "Runtime ERROR: Argument and Parameter sizes in Function '" + funky + "' does not match." ); exit( 1 ); } for( int i = 0; i < params.size(); i++ ) { assignVariable( params.get( i ), args.get( i ).evaluate() ); } ( (ValueFunction) symbolTable.get( funky ) ).getBlock().execute(); return retrieveVariable( "AnyaBASICfunctionReturn123456" ); } public Expression executeFunctionDefinition( String funky, List<String> params, Statement block ) { if( symbolTable.containsKey( funky ) ) { System.out.println( "Runtime ERROR: Function '" + funky + "' already defined." ); exit( 1 ); } symbolTable.put( funky, new ValueFunction( funky, params, block ) ); for( int i = 0; i < params.size(); i++ ) { defineVariable( params.get( i ), new ValueNumber( 0 ) ); } return null; } public Expression executeFunctionCall( String funky, List<Expression> args ) { if( !symbolTable.containsKey( funky ) ) { System.out.println( "Runtime ERROR: Function '" + funky + "' undefined." ); exit( 1 ); } List<String> params = ( (ValueFunction) symbolTable.get( funky ) ).getVariables(); if( params.size() != args.size() ) { System.out.println( "Runtime ERROR: Argument and Parameter sizes in Function '" + funky + "' does not match." ); exit( 1 ); } for( int i = 0; i < params.size(); i++ ) { assignVariable( params.get( i ), args.get( i ).evaluate() ); } ( (ValueFunction) symbolTable.get( funky ) ).getBlock().execute(); return null; } // Executes Array definition // Used for things like // array = {1,2,3,4,5,6,7} // array[10] public Expression executeArrayDefinition( String name, List<Expression> args ) { if( symbolTable.containsKey( name ) ) { System.out.println( "Runtime ERROR: Array '" + name + "' already defined." ); exit( 1 ); } List<Expression> items = new ArrayList<>( args.size() ); for( Expression i : args ) { items.add( i.evaluate() ); } symbolTable.put( name, new ValueArray( name, items ) ); return null; } // Executes Array definition // Used for things like // array[10][20] public Expression executeArray2dDefinition( String name, int rows, int columns, List<Expression> args ) { if( symbolTable.containsKey( name ) ) { System.out.println( "Runtime ERROR: Array '" + name + "' already defined." ); exit( 1 ); } List<Expression> items = new ArrayList<>( args.size() ); for( Expression i : args ) { items.add( i.evaluate() ); } symbolTable.put( name, new ValueArray( name,rows, columns, items ) ); return null; } // Executes struct definition public Expression executeStructDefinition( String name, Statement block, Map<String, Value> fields ) { if( symbolTable.containsKey( name ) ) { System.out.println( "Runtime ERROR: Type '" + name + "' already defined." ); exit( 1 ); } symbolTable.put( name, new ValueStruct( name, fields ) ); block.execute(); return null; } // Executes struct instantiation // Vector2D v // TypeName varname public Expression executeStructInstantiation( String structName, String varName ) { if( !symbolTable.containsKey( structName ) ) { System.out.println( "Runtime ERROR: Type '" + structName + "' undefined." ); exit( 1 ); } Map<String, Value> structfields = ( (ValueStruct) symbolTable.get( structName ) ).getFields(); // Copy values from structfields to varfields Map<String, Value> varfields = new HashMap<>( structfields ); Statement block = userDefinedStructs.get( structName ); if( symbolTable.containsKey( varName ) ) { System.out.println( "Runtime ERROR: Type Variable '" + varName + "' already defined." ); exit( 1 ); } symbolTable.put( varName, new ValueStruct( varName, varfields ) ); block.execute(); return null; } // Executes Array of structsdefinition // Used for things like // Vector2d array[10] public Expression executeArrayStructDefinition( String structName, String arrayName, Expression numElements ) { if( !symbolTable.containsKey( structName ) ) { System.out.println( "Runtime ERROR: Type '" + structName + "' undefined." ); exit( 1 ); } List<Expression> items = new ArrayList<>(); Map<String, Value> structfields = ( (ValueStruct) retrieveVariable( structName ) ).getFields(); for( int i = 0; i < (int) numElements.evaluate().toNumber(); i++ ) { Map<String, Value> varfields = new HashMap<>( structfields ); items.add( new ValueStruct( arrayName, varfields ) ); } if( symbolTable.containsKey( arrayName ) ) { System.out.println( "Runtime ERROR: Array Type Variable '" + arrayName + "' already defined." ); exit( 1 ); } symbolTable.put( arrayName, new ValueArray( arrayName, items ) ); return null; } // Executes 2d Array of structsdefinition // Used for things like // Vector2d array[10][20] public Expression executeArray2dStructDefinition( String structName, String arrayName, Expression numRows, Expression numCols ) { if( !symbolTable.containsKey( structName ) ) { System.out.println( "Runtime ERROR: Type '" + structName + "' undefined." ); exit( 1 ); } List<Expression> items = new ArrayList<>(); Map<String, Value> structfields = ( (ValueStruct) retrieveVariable( structName ) ).getFields(); int rows = numRows.evaluate().toInteger(); int cols = numCols.evaluate().toInteger(); int numElements = rows * cols; for( int i = 0; i < numElements; i++ ) { Map<String, Value> varfields = new HashMap<>( structfields ); items.add( new ValueStruct( arrayName, varfields ) ); } if( symbolTable.containsKey( arrayName ) ) { System.out.println( "Runtime ERROR: Array Type Variable '" + arrayName + "' already defined." ); exit( 1 ); } symbolTable.put( arrayName, new ValueArray( arrayName, rows, cols ,items ) ); return null; } public Expression executeMapDefinition( String name ) { if( symbolTable.containsKey( name ) ) { System.out.println( "Runtime ERROR: Associative Array '" + name + "' already defined." ); exit( 1 ); } symbolTable.put( name, new ValueMap( name ) ); return null; } public Expression executeFree( String name ) { if( symbolTable.containsKey( name ) ) { symbolTable.remove( name ); } else { System.out.println( "Runtime ERROR: Variable or Type '" + name + "' undefined." ); exit( 1 ); } if( userDefinedStructs.containsKey( name ) ) { userDefinedStructs.remove( name ); } return null; } void parse() { match( Token.Type.START_BLOCK ); currentCodeLine++; while( currentToken().getType() != Token.Type.END_BLOCK ) { identifyKeyword( currentToken().getType(), statements ); // load to main AST } match( Token.Type.END_BLOCK ); currentCodeLine++; } // error helper private void printErrorLine() { String codeString = ""; String caretString = ""; int currentErrorTokenIndex = currentStatementIndex; while( currentErrorTokenIndex != errorTokenIndex ) { codeString += tokens.get( currentErrorTokenIndex++ ).getTextOriginal() + " "; } for( int i = 0; i < codeString.length() - 2; i++ ) { caretString += " "; } caretString += " ^"; int tokenCounter = 0; do { codeString += tokens.get( currentErrorTokenIndex++ ).getTextOriginal() + " "; if( ( tokenCounter++ ) > 20 ) break; } while( !isStartOfLine( tokens.get( currentErrorTokenIndex ).getType() ) ); System.out.println( codeString ); System.out.println( caretString ); } // Helper function for error reporting private boolean isStartOfLine( Token.Type type ) { switch( type ) { case START_BLOCK: case END_BLOCK: case PRINT: case WRITE: case DELAY: case WHILE: case IF: case FOR: case REPEAT: case FUNCTION: // function definition case RETURN: // function definition case SCREEN: // function definition case SCREEN_CLEAR: // function definition case SCREEN_FLIP: // function definition case DRAW_LINE: // function definition case DRAW_ELLIPSE: // function definition case DRAW_STRING: // function definition case SET_COLOR: // function definition case SET_BLENDMODE: case LOAD_IMAGE: // function definition case DRAW_IMAGE: // function definition case DRAW_TRANSFORMED_IMAGE: // function definition case DRAW_FANCY_LINE: case DRAW_SCROLLED_IMAGE: case GET_SUB_IMAGE: // function definition case SOUND_INIT: case SOUND_LOAD: case SOUND_PAUSE: case SOUND_STOP: case SOUND_PLAY: case FREE: case SWAP: // swap case EXIT: // function definition case VAR: // function definition case INPUT: // function definition case BREAK: return true; case KEYWORD: if( nextToken().getType() == Token.Type.ASSIGNMENT ) { return true; } else if( nextToken().getType() == Token.Type.LEFT_BRACKET ) { return true; } else //function call as a statement { return true; } } return false; } // recognizers private boolean isAddOp( Token.Type type ) { return ( type == Token.Type.PLUS ) || ( type == Token.Type.MINUS ); } private boolean isMulOp( Token.Type type ) { return ( type == Token.Type.ASTERISK ) || ( type == Token.Type.SLASH ) || ( type == Token.Type.PERCENT ); } //private boolean isUnaryOp(Token.Type type) //{ // return type == Token.Type.NOT; //} private boolean isLogicalOp( Token.Type type ) { return type == Token.Type.OR || type == Token.Type.AND; } private boolean isMultiDigitOp( Token.Type type ) { return type == Token.Type.LESSEQUAL || type == Token.Type.GREATEREQUAL; } private boolean isRelationalOp( Token.Type type ) { boolean lgOps = type == Token.Type.LESS || type == Token.Type.GREATER; boolean eqOps = type == Token.Type.EQUAL || type == Token.Type.NOTEQUAL; return eqOps || lgOps || isMultiDigitOp( type ) || isLogicalOp( type ); } public void defineVariable( String varName, Value value ) { symbolTable.put( varName, value ); } public void assignVariable( String varName, Value value ) { if( !symbolTable.containsKey( varName ) ) { System.out.println( "Runtime ERROR: Variable '" + varName + "' undefined." ); exit( 1 ); } symbolTable.put( varName, value ); } // Assign value to array public void assignArrayVariable( String varName, Expression index, Value value ) { if( !symbolTable.containsKey( varName ) ) { System.out.println( "Runtime ERROR: Array variable '" + varName + "' undefined." ); exit( 1 ); } symbolTable.get( varName ).toArray().set( (int) index.evaluate().toNumber(), value ); } // Assign value to 2d array public void assignArray2dVariable( String varName, Expression row, Expression column, Value value ) { if( !symbolTable.containsKey( varName ) ) { System.out.println( "Runtime ERROR: Array variable '" + varName + "' undefined." ); exit( 1 ); } int r = row.evaluate().toInteger(); int c = column.evaluate().toInteger(); ValueArray array = ((ValueArray)symbolTable.get( varName )); array.setValue( r, c, value ); } // Assign values to a field struct struct // v.x = 5 public void assignStructFieldVariable( String structName, String fieldName, Value value ) { if( !symbolTable.containsKey( structName ) ) { System.out.println( "Runtime ERROR: Type Variable '" + structName + "' undefined." ); exit( 1 ); } Map<String, Value> fields = ( (ValueStruct) symbolTable.get( structName ) ).getFields(); if( !fields.containsKey( fieldName ) ) { System.out.println( "Runtime ERROR: Field Variable '" + structName + "." + fieldName + "' undefined." ); exit( 1 ); } fields.put( fieldName, value ); } // Assign values to a struct field in an array element // v[10].x = 5 public void assignArrayStructFieldVariable( String arrayName, Expression index, String fieldName, Value value ) { if( !symbolTable.containsKey( arrayName ) ) { System.out.println( "Runtime ERROR: Array Variable '" + arrayName + "' undefined." ); exit( 1 ); } Map<String, Value> fields = ( (ValueStruct) symbolTable.get( arrayName ).toArray().get( (int) index.evaluate().toNumber() ) ).getFields(); if( !fields.containsKey( fieldName ) ) { String idx = "[" + ( (int) index.evaluate().toNumber() ) + "]"; System.out.println( "Runtime ERROR: Field Variable '" + arrayName + idx + "." + fieldName + "' undefined." ); exit( 1 ); } fields.put( fieldName, value ); } // Assign values to a struct field in an array element // v[10].x = 5 public void assignArray2dStructFieldVariable( String arrayName, Expression row, Expression col, String fieldName, Value value ) { if( !symbolTable.containsKey( arrayName ) ) { System.out.println( "Runtime ERROR: Array Variable '" + arrayName + "' undefined." ); exit( 1 ); } int r = row.evaluate().toInteger(); int c = col.evaluate().toInteger(); ValueArray array = ((ValueArray)symbolTable.get( arrayName )); Map<String, Value> fields = ((ValueStruct)array.getValue( r,c )).getFields(); if( !fields.containsKey( fieldName ) ) { String idx = "[" + r + "]" + "[" + c + "]"; System.out.println( "Runtime ERROR: Field Variable '" + arrayName + idx + "." + fieldName + "' undefined." ); exit( 1 ); } fields.put( fieldName, value ); } // Assign value to map public void assignMapVariable( String varName, Expression index, Value value ) { if( !symbolTable.containsKey( varName ) ) { System.out.println( "Runtime ERROR: Map variable '" + varName + "' undefined." ); exit( 1 ); } ((ValueMap)symbolTable.get( varName )).toMap().put( index.evaluate().toString(), value ); } // Add a typedef with its block for execution later // used for // Vector2D v public void addUserStruct( String structName, Statement blockStatements ) { userDefinedStructs.put( structName, blockStatements ); } public void incrementCodeLine() { currentCodeLine++; } public Expression retrieveVariable( String varName ) { //System.out.println("Variable name:" + symbolTable.get( varName )); if( !symbolTable.containsKey( varName ) ) { System.out.println( "Runtime ERROR: variable '" + varName + "' undefined." ); exit( 1 ); } return symbolTable.get( varName ); } public Expression retrieveArrayVariable( String varName, Expression index ) { //System.out.println("Variable name:" + symbolTable.get( varName )); if( !symbolTable.containsKey( varName ) ) { System.out.println( "Runtime ERROR: variable '" + varName + "' undefined." ); exit( 1 ); } return symbolTable.get( varName ).toArray().get( (int) index.evaluate().toNumber() ); } public Expression retrieveArray2dVariable( String varName, Expression row, Expression col ) { if( !symbolTable.containsKey( varName ) ) { System.out.println( "Runtime ERROR: variable '" + varName + "' undefined." ); exit( 1 ); } int r = row.evaluate().toInteger(); int c = col.evaluate().toInteger(); ValueArray array = ((ValueArray)symbolTable.get( varName )); return array.getValue( r, c ); } public Expression retrieveStructVariable( String varName, String fieldName ) { if( !symbolTable.containsKey( varName ) ) { System.out.println( "Runtime ERROR: variable '" + varName + "' undefined." ); exit( 1 ); } return ( (ValueStruct) symbolTable.get( varName ) ).getFields().get( fieldName ); } public Expression retrieveArrayStructFieldVariable( String varName, Expression index, String fieldName ) { if( !symbolTable.containsKey( varName ) ) { System.out.println( "Runtime ERROR: variable '" + varName + "' undefined." ); exit( 1 ); } return ( (ValueStruct) symbolTable.get( varName ).toArray().get( index.evaluate().toInteger() ) ).getFields().get( fieldName ); } public Expression retrieveArray2dStructFieldVariable( String varName, Expression row, Expression col, String fieldName ) { if( !symbolTable.containsKey( varName ) ) { System.out.println( "Runtime ERROR: variable '" + varName + "' undefined." ); exit( 1 ); } int r = row.evaluate().toInteger(); int c = col.evaluate().toInteger(); ValueArray array = ((ValueArray)symbolTable.get( varName )); return ((ValueStruct)array.getValue( r, c )).getFields().get( fieldName ); } public Expression retrieveMapVariable( String varName, Expression index ) { if( !symbolTable.containsKey( varName ) ) { System.out.println( "Runtime ERROR: Associative Array variable '" + varName + "' undefined." ); exit( 1 ); } return ((ValueMap)symbolTable.get( varName )).toMap().get( index.evaluate().toString() ); } public Graphics getGraphics() { return graphics; } List<Statement> getStatements() { return statements; } void setTokens( List<Token> tokens ) { this.tokens = tokens; currentTokenIndex = 0; } public int getCurrentTokenIndex() { return currentTokenIndex; } public void setCurrentTokenIndex( int currentTokenIndex ) { this.currentTokenIndex = currentTokenIndex; } // function change local var public Token.Type reTokenizeLocalFunctionVariables( Token.Type type, String functionName, List<String> paramNames ) { Token.Type res; if( type == Token.Type.KEYWORD ) { String text = currentToken().getText(); if( paramNames.contains( text ) ) { tokens.set( currentTokenIndex, new Token(Token.Type.KEYWORD, functionName + "zero_12345" + text )); } res = currentToken().getType(); match( Token.Type.KEYWORD ); } else if( currentToken().getType() == Token.Type.DOT ) { res = currentToken().getType(); eatToken( 2 ); } else { res = currentToken().getType(); eatToken( 1 ); } return res; } }