UNIVERSITY OF NORTH FLORIDA Graphfruit A Graphing Language for the Modern Age Jamie Gordon & Emroy Wingard 11/19/2013 Graphfruit Introduction The Graphfruit language is designed to use the flexibility of a programming language to design and implement graphs. The programming language uses design ideas from C-like languages, but excludes the bracketing syntax to make code more readable. Procedural Language Graphfruit is a procedural language. Procedures can be programmatically created to manipulate numbers and equations for graphing later on. In this case, Graphfruit is a high-level abstraction of the graphing system. Design Goals Graphfruit was designed to be a high-level language that would allow programmers to easily make graphs. It was also designed so that code could be easily moved from program to program and be reused for different graphing situations. A list of specific goals follow. High-level of abstraction allowing programmers to ignore such factors as data calculation Ability to graph more than one equation on a single set of axes Ability to program piecewise functions Accurate graphs Include basic operations for equation construction Allow programmers to implement any operations that are not included in the specification procedurally Remove hindrances to readability found in other C-derivatives, like brackets and semicolons instead using newlines and the keyword end. Graphfruit Language Design Data Types There are two data types. 1. Numbers – Numbers in Graphfruit are just that, numerical values. Graphfruit does not distinguish between integers and floating point numbers. (In actuality, they are both treated as doubles in the backend). The reason for this is that once the graphing is done, 2000 points of data are calculated to graph the function. These will most likely be floating point numbers themselves. 2. Equations – In Graphfruit equations are what will be graphed at the end of the program. They are delimited with accents, ``. Equations must be expressed in terms of x. Equations are basically Strings, but with a few other behaviors. For example, all the operations (with the exception of modulus) can be applied to equations. What this does, in effect, is combine equations together using the operation. For example, `x ^ 3 – x ^ 2` + `x * 3` Will yield: `x ^ 3 – x ^ 2 + x * 3` `3 * x + 4` / `x ^ 2` Will yield: `( 3 * x + 4 ) / ( x ^ 2 )` Arrays You can also use arrays with Graphfruit. You can make arrays of numbers or equations. Graphfruit does not support returning arrays or graphing arrays, the latter of which does not make much sense to do. Data Type Use The way to declare variables or equations in Graphfruit is to use the data type specifier in front of the variable id. This is also how you declare you functions using a type specifier. There are no void functions in Graphfruit. Because the whole point of the language is to output graphs, using functions that do not return a value makes no sense. All graphing is done in main(). Another note, variables must be declared at the beginning of a function or control statement body, before all other statements are given. Global variables are allowed. Variable Declaration num factor equation eq Function Declaration num factorial(num factor) equation sine() Array Declaration num factor[3] equation eq[5] Comments Comments are specified using #. This symbol comments out the whole line. Operators Below is a list of all operators that can be used on the data types. If an operator is performed on a number and equation together it defaults to the equation behavior. An operation cannot be performed on a whole array. Operator Number Equation + * / ^ Adds numbers Subtracts numbers Multiplies numbers Divides numbers Raises number to power Modulus for number Concatenates equations or numbers using addition Concatenates using subtraction Concatenates using multiplication, with parentheses Concatenates using division, with parentheses Concatenates using exponentiation, with parentheses mod Error Functions As stated previously, all methods (excluding main) return a value. Main is special and will be discussed in the next section. In addition, there are no curly brackets used. Statements are ended with newlines rather than semicolons, as shown below. equation stuff(num number) equation eq = `x` if (number > 0) eq = eq ^ `2` else eq = eq ^ `-2` end return eq end Specifics of Main In Graphfruit, the main declaration holds a very special significance. First of all it is the only void function and is declared thusly: def main() … end However, this is not the only special thing about main. This is where you specify the equations that you wish to graph, using the graph keyword rather than return: def main() … graph `x` from x = 0 to 10, y = 10 to 10 end The from keyword specifies the values of x to use in the graph and how tall the graph should be. The entire from syntax can be disregarded, in which case -10 to 10 is assumed for both x and y. You can also choose to only specify x or y. Multiple graph statements can be used. If the ranges overlap, you will get overlapping graphs. If they do not overlap, you will get a piecewise graph, like below. This is the result of the example in Appendix D. The size of the graph is determined by the Gaph Frame. The graph frame chooses the most extreme values provided to graph the function. For example, if one said: Graph `x` from x = -7 to 5, y= -8 to 4 Graph `x^2` from x=-10 to 0, y = -5 to 10 The x-axis would range from -10 to 5 and the y-axis would range from -8 to 10. Details about Compilation and Execution A helpful diagram is included (Appendix A) to show the full process. Lexical Analysis A text file is fed into the compiler. The compiler sends the file to the lexical analyzer. The lexical analyzer splits the file into useful tokens and labels the tokens for use in the compiler. Compilation The compiler then uses these tokens to parse the file. If the file passes this syntactic analysis—based on the grammar in Appendix B—the file moves on to semantic analysis. In semantic analysis, it is made sure that the correct expressions are used at the correct time, such as checking that data types are used in a legal manner. Intermediate Code Generation Once it passes semantic analysis, intermediate code generation can take place. Intermediate code generation creates a .gph file. The file name is simply the original file name appended with .gph. This means that the GraphfruitCompiler.java compiles the code, and the .gph code can be run multiple times without the need for recompiling the code. Execution Code is run using the Graphfruit execution environment, using the Graphfruit.java file. This program interprets and runs the quadruples. This will generate a number of equations, with x and y ranges. Equation Interpretation These equations are sent to the GraphFrame, which then takes over. The GraphFrame sends the String equations to the EquationLexicalAnalyzer, which generates String tokens for the EquationParser. The EquationParser interprets the tokens to create sets of x-points and y-points. The GraphFrame then takes these sets of points and graphs them. Conclusion In summary, code is programmed into a text file. This text file is then run using java GraphfruitCompiler textfile. This produces a file called textfile.gph. This graph file is run using java Graphfruit textfile.gph. This produces a set of strings, which is also tokenized and then interpreted. A graph is then created. Troubleshooting It may also be necessary to compile the source first. Run javac *.java in the source directory. Then, to run the compiler type java compiler.GraphfruitCompiler textfile and java execution.Graphfruit textfile.gph. This may fix any errors you may have. You need to be able to run java swing resources. Criteria for Language Efficiency Graphfruit execution is interesting. Code is compiled into an intermediate language. This means that code should be faster than typical execution. However, when the quadruples are executed, the code produces equations, which are then interpreted. Theoretically, the equations that are produced could also be outputted as a text file (so they wouldn’t have to be reproduced on each execution). However, it was decided that for future work (adding in user interactions through console input), this should not be included. The language is also written in Java, so it is dependent on the performance of the JVM. Simplicity Readability I believe that the code is very simple to read. With the exclusion of things like brackets, code is more easily understood. The graph keyword syntax is very naturally read. Having the graph expressions on the bottom of the program makes a lot of sense, given that it will be the final result of your program. Writability Someone familiar with C-like syntax should be able to easily write code, and the addition of the graph reserved word should be easy to pick up because its format is written very naturally. Orthogonality As there are only two data types, orthogonality is well preserved. All operations (except modulus) can be performed on both data types. While none can be performed on arrays themselves, arrays typically cannot have operations performed on them in C-like languages. Portability This code can be run on any environment Java can be run on, provided swing can be run with it. It has no real meaning in a console environment. The user will be informed of this fact if execution is attempted in a console environment. Future Work Because of the time restraints, not all ideas could be executed. Some of these ideas follow. More operations Currently, the language only supports addition, subtraction, multiplication, division, modulus, and exponents. Graphing supports all the same operators, excluding modulus. Many other binary operations could be relatively easily added to the language specification, given time. However, at the moment, only binary operations are currently supported in the grammar. Adding unary operations, like trigonometric functions (sine, cosine, etc.) or logarithms, cannot be as easily inserted into the grammar. While these would be useful, especially in the equation parser, time concerns were an issue. The good news is that many of these operations can be implemented using the current grammar programmatically, with a little research. This document shows an example of how to approximate sine using a Taylor series (Appendix D). Interactivity Program interactivity was shelved for Graphfruit. It would be nice to take in user input as well as programs. Another version of interactivity might be to allow users to click on parts of the graph and get the x and y coordinates. Graph Display Graphing has many features even now, but there are other features that could be added. For example, adding numbers to the number lines might be useful to the user. Another useful programming feature could be to allow the programmer to specify coloration of the axis and the lines. The specification could be updated to add the ability to program multiple axes in different panels in a single frame, or even have multiple windows open. Conclusion There are many options that could have been included into the Graphfruit specification, but the important features had to be chosen so that the project could be completed in the allotted time. Appendix A - Compilation and Execution Lexical Analysis Tokens Parsing Semantic Analysis Outputs Filename.gph GraphfruitCompiler.java Code Generation Quadruples Graphfruit.java Execution Equations Graph GraphFrame.java Graphing Equations Points EquationParser.java Equation Parser Tokens Equation Lexical Analysis EquationLexicalAnalyzer.java Appendix B - Graphfruit Grammar Prgm -> DeclarList MainDeclar | MainDeclar DeclarList -> Declar DeclarList' DeclarList' -> Declar DeclarList' | @ Declar -> DataType ID Declar' Declar' -> VarDeclar' AssignmentStmt newline | FunDeclar FunDeclar -> ( ParamList ) Body end newline DataType -> num | equation ParamList -> Param ParamList' | @ ParamList' -> , Param ParamList' | @ Param -> DataType ID Param' Param' -> [ ] | @ VarDeclar -> DataType ID VarDeclar' AssignmentStmt newline VarDeclar' -> [ NUM ] | @ AssignmentStmt -> = Expression | @ Body -> newline LocalDeclarList StmtList LocalDeclarList -> LocalDeclarList' LocalDeclarList' -> VarDeclar LocalDeclarList' | @ StmtList -> StmtList' StmtList' -> Stmt StmtList' | @ Stmt -> ExpressionStmt | SelectionStmt | IterationStmt | ReturnStmt ExpressionStmt -> Expression newline | ; SelectionStmt -> if ( Expression ) Body SelectionStmt' SelectionStmt' -> else Body end newline | end newline IterationStmt -> while ( Expression ) Body end ReturnStmt -> return Expression newline Expression -> ID Expression' | Factor' Expression''' Expression' -> Var' Expression'' | ( Args ) Expression''' Expression'' -> = Expression | Expression''' Expression''' -> ExponentialExpression' MultiplicativeExpression' AdditiveExpression' SimpleExpression' Var' -> [ Expression ] | @ SimpleExpression' -> Relop AdditiveExpression | @ Relop -> <= | < | > | >= | == | != AdditiveExpression -> MultiplicativeExpression AdditiveExpression' AdditiveExpression' -> Addop MultiplicativeExpression AdditiveExpression' | @ Addop -> + | MultiplicativeExpression -> ExponentialExpression MultiplicativeExpression' MultiplicativeExpression' -> Mulop ExponentialExpression MultiplicativeExpression' | @ Mulop -> * | / | mod ExponentialExpression -> Factor ExponentialExpression' ExponentialExpression' -> Exop Factor ExponentialExpression' | @ Exop -> ^ Factor -> ID IndexCall | Factor' Factor' -> ( Expression ) | NUM | EQ IndexCall -> Var' | ( Args ) Args -> ArgList | @ ArgList -> Expression ArgList' ArgList' -> , Expression ArgList' | @ MainDeclar -> def main ( ) Body GraphList end GraphList -> Graph GraphList' GraphList' -> newline Graph GraphList' | @ Graph -> graph Expression GraphOpts GraphOpts -> from Opts | @ Opts -> XRange YOpts | YRange XOpts XRange -> x = Range YRange -> y = Range XOpts -> , XRange | @ YOpts -> , YRange | @ Range -> Expression to Expression Appendix C - Typical Executable (.gph) File Index Operator Operand1 Operand2 Result 0 ALLOC NUM ~ global 1 ALLOC NUM 3 snuff 2 ALLOC EQUATION ~ xl 3 ADD `x` 1 t0 4 ASGN t0 ~ xl 5 FUNC getNum NUM 1 6 PARAM NUM ~ funNum 7 BLOCK ~ ~ ~ 8 ASGN 2 ~ snuff[2] 9 MULT funNum 3 t1 10 RETURN ~ ~ t1 11 END ~ ~ ~ 12 FUNC stuff EQUATION 1 13 PARAM NUM ~ number 14 BLOCK ~ ~ ~ 15 ALLOC EQUATION ~ eq 16 ASGN `x` ~ eq 17 COMP number 0 t2 18 BRGT t2 ~ 20 19 BR ~ ~ 25 20 BLOCK ~ ~ ~ 21 EXP eq `2` t3 22 ASGN t3 ~ eq 23 END ~ ~ ~ 24 BR ~ ~ 29 25 BLOCK ~ ~ ~ 26 EXP eq `-2` t4 27 ASGN t4 ~ eq 28 END ~ ~ ~ 29 RETURN ~ ~ eq 30 END ~ ~ ~ 31 FUNC factorial NUM 1 32 PARAM NUM ~ factor 33 BLOCK ~ ~ ~ 34 ALLOC NUM ~ index 35 ASGN factor ~ index 36 ALLOC NUM ~ product 37 ASGN 1 ~ product 38 COMP index 0 t5 39 BRGT t5 ~ 41 40 BR ~ ~ 48 41 BLOCK ~ ~ ~ 42 MULT product index t6 43 ASGN t6 ~ product 44 SUB index 1 t7 45 ASGN t7 ~ index 46 END ~ ~ ~ 47 BR ~ ~ 38 48 RETURN ~ ~ product 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 END ~ ~ ~ FUNC sine EQUATION 0 BLOCK ~ ~ ~ ALLOC NUM ~ index ASGN 1 ~ index ALLOC EQUATION ~ eq ASGN `x` ~ eq COMP index 15 t8 BRLT t8 ~ 59 BR ~ ~ 76 BLOCK ~ ~ ~ EXP -1 index t9 MULT 2 index t10 ADD t10 1 t11 EXP `x` t11 t12 MULT t9 t12 t13 CALL factorial 1 t14 MULT 2 index t15 ADD t15 1 t16 ARG t16 factorial factor DIV t13 t14 t17 ADD eq t17 t18 ASGN t18 ~ eq ADD index 1 t19 ASGN t19 ~ index END ~ ~ ~ BR ~ ~ 56 RETURN ~ ~ eq END ~ ~ ~ FUNC main void 0 BLOCK ~ ~ ~ ALLOC EQUATION ~ funEq CALL getNum 1 t20 ARG 2 getNum funNum ASGN t20 ~ snuff[1] CALL stuff 1 t21 CALL getNum 1 t22 ARG 2 getNum funNum ARG t22 stuff number ADD t21 `x` t23 ADD t21 snuff[1] t24 ASGN t24 ~ funEq CALL sine 0 t25 XOPT -5 -1 ~ YOPT -10 10 ~ GRAPH ~ ~ t25 XOPT -1 5 ~ YOPT -10 10 ~ GRAPH ~ ~ funEq Appendix D – Sample Program num arr[5] num getNum(num funNum) return funNum * 3 end equation stuff(num number) equation eq = `x` if (number > 0) eq = eq ^ `2` else eq = eq ^ `-2` end return eq end num factorial(num factor) num index = factor num product = 1 while (index > 0) product = product * index index = index - 1 end return product end equation sine() num index = 0 equation eq = `x` # Taylor series while (index < 4) eq = eq + (-1) ^ index * `x` ^ (2 * index + 1) / factorial(2 * index + 1) index = index + 1 end return eq end def main() equation funEq = stuff(getNum(2)) + `x` arr[2] = getNum(3) funEq = funEq + arr[2] graph sine() from x = -5 to -1, y = -10 to 10 graph funEq from x = -1 to 5, y = -5 to 5 end