Getting ANTLR to generate a script interpreter? - java

Say I have the following Java API that all packages up as blocks.jar:
public class Block {
private Sting name;
private int xCoord;
private int yCoord;
// Getters, setters, ctors, etc.
public void setCoords(int x, int y) {
setXCoord(x);
setYCoord(y);
}
}
public BlockController {
public static moveBlock(Block block, int newXCoord, int newYCoord) {
block.setCooords(newXCoord, newYCoord);
}
public static stackBlocks(Block under, Block onTop) {
// Stack "onTop" on top of "under".
// Don't worry about the math here, this is just for an example.
onTop.setCoords(under.getXCoord() + onTop.getXCoord(), under.getYCoord());
}
}
Again, don't worry about the math and the fact that (x,y) coordinates don't accurately represent blocks in 3D space. The point is that we have Java code, compiled as a JAR, that performs operations on blocks. I now want to build a lightweight scripting language that allows a non-programmer to invoke the various block API methods and manipulate blocks, and I want to implement its interpreter with ANTLR (latest version is 4.3).
The scripting language, we'll call it BlockSpeak, might look like this:
block A at (0, 10) # Create block "A" at coordinates (0, 10)
block B at (0, 20) # Create block "B" at coordinates (0, 20)
stack A on B # Stack block A on top of block B
This might be equivalent to the following Java code:
Block A, B;
A = new Block(0, 10);
B = new Block(0, 20);
BlockController.stackBlocks(B, A);
So the idea is that the ANTLR-generated interpreter would take a *.blockspeak script as input, and use the commands in this script to invoke blocks.jar API operations. I read the excellent Simple Example which creates a simple calculator using ANTLR. However in that link, there is an ExpParser class with an eval() method:
ExpParser parser = new ExpParser(tokens);
parser.eval();
The problem here is that, in the case of the calculator, the tokens represent a mathematical expression to evaluate, and eval() returns the evaluation of the expression. In the case of an interpreter, the tokens would represent my BlockSpeak script, but calling eval() shouldn't evaluate anything, it should know how to map the various BlockSpeak commands to Java code:
BlockSpeak Command: Java code:
==========================================
block A at (0, 10) ==> Block A = new Block(0, 10);
block B at (0, 20) ==> Block B = new Block(0, 20);
stack A on B ==> BlockController.stackBlocks(B, A);
So my question is, where do I perform this "mapping"? In other words, how do I instruct ANTLR to call various pieces of code (packaged inside blocks.jar) when it encounters particular grammars in the BlockSpeak script? More importantly, can someone give me a pseudo-code example?

I would simply evaluate the script on the fly, not generate Java source files which need to be compiled themselves again.
With ANTLR 4 it is highly recommended to keep the grammar and target specific code separate from each other and put any target specific code inside a tree-listener or -visitor.
I will give a quick demo how to use a listener.
A grammar for your example input could look like this:
File: blockspeak/BlockSpeak.g4
grammar BlockSpeak;
parse
: instruction* EOF
;
instruction
: create_block
| stack_block
;
create_block
: 'block' NAME 'at' position
;
stack_block
: 'stack' top=NAME 'on' bottom=NAME
;
position
: '(' x=INT ',' y=INT ')'
;
COMMENT
: '#' ~[\r\n]* -> skip
;
INT
: [0-9]+
;
NAME
: [a-zA-Z]+
;
SPACES
: [ \t\r\n] -> skip
;
Some supporting Java classes:
File: blockspeak/Main.java
package blockspeak;
import org.antlr.v4.runtime.ANTLRInputStream;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.tree.ParseTreeWalker;
import java.util.Scanner;
public class Main {
public static void main(String[] args) throws Exception {
Scanner keyboard = new Scanner(System.in);
// Some initial input to let the parser have a go at.
String input = "block A at (0, 10) # Create block \"A\" at coordinates (0, 10)\n" +
"block B at (0, 20) # Create block \"B\" at coordinates (0, 20)\n" +
"stack A on B # Stack block A on top of block B";
EvalBlockSpeakListener listener = new EvalBlockSpeakListener();
// Keep asking for input until the user presses 'q'.
while(!input.equals("q")) {
// Create a lexer and parser for `input`.
BlockSpeakLexer lexer = new BlockSpeakLexer(new ANTLRInputStream(input));
BlockSpeakParser parser = new BlockSpeakParser(new CommonTokenStream(lexer));
// Now parse the `input` and attach our listener to it. We want to reuse
// the same listener because it will hold out Blocks-map.
ParseTreeWalker.DEFAULT.walk(listener, parser.parse());
// Let's see if the user wants to continue.
System.out.print("Type a command and press return (q to quit) $ ");
input = keyboard.nextLine();
}
System.out.println("Bye!");
}
}
// You can place this Block class inside Main.java as well.
class Block {
final String name;
int x;
int y;
Block(String name, int x, int y) {
this.name = name;
this.x = x;
this.y = y;
}
void onTopOf(Block that) {
// TODO
}
}
This main class is pretty self explanatory with the inline comments. The tricky part is what the listener is supposed to look like. Well, here it is:
File: blockspeak/EvalBlockSpeakListener.java
package blockspeak;
import org.antlr.v4.runtime.misc.NotNull;
import java.util.HashMap;
import java.util.Map;
/**
* A class extending the `BlockSpeakBaseListener` (which will be generated
* by ANTLR) in which we override the methods in which to create blocks, and
* in which to stack blocks.
*/
public class EvalBlockSpeakListener extends BlockSpeakBaseListener {
// A map that keeps track of our Blocks.
private final Map<String, Block> blocks = new HashMap<String, Block>();
#Override
public void enterCreate_block(#NotNull BlockSpeakParser.Create_blockContext ctx) {
String name = ctx.NAME().getText();
Integer x = Integer.valueOf(ctx.position().x.getText());
Integer y = Integer.valueOf(ctx.position().y.getText());
Block block = new Block(name, x, y);
System.out.printf("creating block: %s\n", name);
blocks.put(block.name, block);
}
#Override
public void enterStack_block(#NotNull BlockSpeakParser.Stack_blockContext ctx) {
Block bottom = this.blocks.get(ctx.bottom.getText());
Block top = this.blocks.get(ctx.top.getText());
if (bottom == null) {
System.out.printf("no such block: %s\n", ctx.bottom.getText());
}
else if (top == null) {
System.out.printf("no such block: %s\n", ctx.top.getText());
}
else {
System.out.printf("putting %s on top of %s\n", top.name, bottom.name);
top.onTopOf(bottom);
}
}
}
The listener above has 2 methods defined that map to the following parser rules:
create_block
: 'block' NAME 'at' position
;
stack_block
: 'stack' top=NAME 'on' bottom=NAME
;
Whenever the parser "enters" such a parser rule, the corresponding method inside the listener will be called. So, whenever enterCreate_block (the parser enters the create_block rule) is called, we create (and save) a block, and when enterStack_block is called, we retrieve the 2 block involved in the operation, and stack one on top of the other.
To see the 3 classes above in action, download ANTLR 4.4 inside the directory that holds the blockspeak/ directory with the .g4 and .java files.
Open a console and perform the following 3 steps:
1. generate the ANTLR files:
java -cp antlr-4.4-complete.jar org.antlr.v4.Tool blockspeak/BlockSpeak.g4 -package blockspeak
2. compile all Java sources files:
javac -cp ./antlr-4.4-complete.jar blockspeak/*.java
3. Run the main class:
3.1. Linux/Mac
java -cp .:antlr-4.4-complete.jar blockspeak.Main
3.2. Windows
java -cp .;antlr-4.4-complete.jar blockspeak.Main
Here is an example session of running the Main class:
bart#hades:~/Temp/demo$ java -cp .:antlr-4.4-complete.jar blockspeak.Main
creating block: A
creating block: B
putting A on top of B
Type a command and press return (q to quit) $ block X at (0,0)
creating block: X
Type a command and press return (q to quit) $ stack Y on X
no such block: Y
Type a command and press return (q to quit) $ stack A on X
putting A on top of X
Type a command and press return (q to quit) $ q
Bye!
bart#hades:~/Temp/demo$
More info on tree listeners: https://theantlrguy.atlassian.net/wiki/display/ANTLR4/Parse+Tree+Listeners

I would personally write a grammar to generate a Java program for each script that you could then compile (along with your jar) and run independently... i.e., a 2-step process.
For example, with something like the following simple grammar (which I haven't tested and I am sure you would need to extend and adapt), you could replace the parser.eval() statement in that example with parser.program(); (also substituting "BlockSpeak" for "Exp" throughout) and it should spit out Java code that matches the script to stdout, which you could redirect into a .java file, compile (together with the jar) and run.
BlockSpeak.g:
grammar BlockSpeak;
program
#init { System.out.println("//import com.whatever.stuff;\n\npublic class BlockProgram {\n public static void main(String[] args) {\n\n"); }
#after { System.out.println("\n } // main()\n} // class BlockProgram\n\n"); }
: inss=instructions { if (null != $inss.insList) for (String ins : $inss.insList) { System.out.println(ins); } }
;
instructions returns [ArrayList<String> insList]
#init { $insList = new ArrayList<String>(); }
: (instruction { $insList.add($instruction.ins); })*
;
instruction returns [String ins]
: ( create { $ins = $create.ins; } | move { $ins = $move.ins; } | stack { $ins = $stack.ins; } ) ';'
;
create returns [String ins]
: 'block' id=BlockId 'at' c=coordinates { $ins = " Block " + $id.text + " = new Block(" + $c.coords + ");\n"; }
;
move returns [String ins]
: 'move' id=BlockId 'to' c=coordinates { $ins = " BlockController.moveBlock(" + $id.text + ", " + $c.coords + ");\n"; }
;
stack returns [String ins]
: 'stack' id1=BlockId 'on' id2=BlockId { $ins = " BlockController.stackBlocks(" + $id1.text + ", " + $id2.text + ");\n"; }
;
coordinates returns [String coords]
: '(' x=PosInt ',' y=PosInt ')' { $coords = $x.text + ", " + $y.text; }
;
BlockId
: ('A'..'Z')+
;
PosInt
: ('0'..'9') ('0'..'9')*
;
WS
: (' ' | '\t' | '\r'| '\n') -> channel(HIDDEN)
;
(Note that for simplicity this grammar requires semi-colons to separate each instruction.)
There are of course other ways to do this sort of thing, but this seems like the simplest to me.
Good luck!
Update
So I went ahead and "finished" my original post (fixing a few bugs in the above grammar) and testing it on a simple script.
Here is the .java file I used to test the above grammar (taken from the code stubs you posted above). Note that in your situation, you would probably want to make the script filename (in my code "script.blockspeak") into a command line parameter. Also, of course the Block and BlockController classes would instead come from your jar.
BlockTest.java:
import org.antlr.v4.runtime.*;
class Block {
private String name;
private int xCoord;
private int yCoord;
// Other Getters, setters, ctors, etc.
public Block(int x, int y) { xCoord = x; yCoord = y; }
public int getXCoord() { return xCoord; }
public int getYCoord() { return yCoord; }
public void setXCoord(int x) { xCoord = x; }
public void setYCoord(int y) { yCoord = y; }
public void setCoords(int x, int y) {
setXCoord(x);
setYCoord(y);
}
}
class BlockController {
public static void moveBlock(Block block, int newXCoord, int newYCoord) {
block.setCoords(newXCoord, newYCoord);
}
public static void stackBlocks(Block under, Block onTop) {
// Stack "onTop" on top of "under".
// Don't worry about the math here, this is just for an example.
onTop.setCoords(under.getXCoord() + onTop.getXCoord(), under.getYCoord());
}
}
public class BlocksTest {
public static void main(String[] args) throws Exception {
ANTLRFileStream in = new ANTLRFileStream("script.blockspeak");
BlockSpeakLexer lexer = new BlockSpeakLexer(in);
CommonTokenStream tokens = new CommonTokenStream(lexer);
BlockSpeakParser parser = new BlockSpeakParser(tokens);
parser.program();
}
}
And here are the command lines I used (on my MacBook Pro):
> java -jar antlr-4.4-complete.jar BlockSpeak.g
> javac -cp .:antlr-4.4-complete.jar *.java
> java -cp .:antlr-4.4-complete.jar BlocksTest > BlockProgram.java
This was the input script:
script.blockspeak:
block A at (0, 10);
block B at (0, 20);
stack A on B;
And this was the output:
BlockProgram.java:
//import com.whatever.stuff;
public class BlockProgram {
public static void main(String[] args) {
Block A = new Block(0, 10);
Block B = new Block(0, 20);
BlockController.stackBlocks(A, B);
} // main()
} // class BlockProgram
You would of course then have to compile and run BlockProgram.java for each script.
In answer to one of the questions in your comment (#3), there are a couple more complex options I first contemplated that might streamline your "user experience".
(A) Instead of using the grammar to generate a java program that you then have to compile and run, you could embed the calls to the BlockController directly into the ANTLR actions. Where I created strings and passed them up from one non-terminal to the next, you could have java code there directly doing your Block commands whenever an instruction rule is recognized. This would require a bit more complexity with respect to the ANTLR grammar and imports, but it's technically doable.
(B) If you were to do option A, you could then go a step further and create an interactive interpreter ("shell"), where the user is presented with a prompt and just types in "blockspeak" commands at the prompt, which are then parsed and executed directly, displaying the results back to the user.
Neither of these options are all that much harder to accomplish in terms of complexity, but they each require doing a lot more coding that would be beyond the scope of a Stack Overflow answer. That's why I opted to present a "simpler" solution here.

The eval() in ExpParser is implemented through method calls; it's just that the calls have shortcut syntax in the form of operators.
As an exercise, change ExpParser adding a Calculator class with (unimplemented) methods for mathematical operators, add(), multiply(), divide(), and so on, and then change the rules to use those methods instead of the operators. Thus, you'll understand the basis of what you need to do for your BlockSpeak interpreter.
additionExp returns [double value]
: m1=multiplyExp {$value = $m1.value;}
( '+' m2=multiplyExp {$value = Calculator.add($value, $m2.value);}
| '-' m2=multiplyExp {$value = Calculator.subtract($value, $m2.value);}
)*
;

Related

Why we have to declare variable outside of if statement in Java [duplicate]

This question already has answers here:
What is 'scope' in Java?
(2 answers)
Closed 2 years ago.
In Java, if we run:
public class HelloWorld{
public static void main(String []args){
if (true) {
int test = 1;
} else {
int test = 2;
}
System.out.println(test);
}
}
It will throw:
HelloWorld.java:9: error: cannot find symbol
System.out.println(test);
^
symbol: variable test
location: class HelloWorld
1 error
However, in php, if we run:
<?php
//Enter your code here, enjoy!
if (true) {
$test = 1;
} else {
$test = 2;
}
echo $test;
It will print 1.
I suspect if that is because Java is strong typed language and php is weak typed language. Can someone give a more deep and lower level explanation?
In Java, the visibility scope of a variable is limited by {}
if (true) {
int test = 1;
} else {
int test = 2;
}
System.out.println(test);// Will fail to compile
It is also important to note the dead code as shown below:
int test;
if (true) {
test = 1;
} else {
test = 2;// Dead code
}
System.out.println(test);
Because of if (true) the else block will never be executed causing test = 2 to become the dead code.
This has nothing to do with difference between static and dynamic typing. The difference here between Java and PHP is variable scope.
In PHP $test = 1; will be part of the global scope as per docs.
In Java int test = 1; will be part of local scope, in your case limited to if block.
Java is "block scoped"; things introduced inside of one { } block go away at the end of that block.
PHP is "function scoped"; things introduced inside of a function last until the end of the function. If you're not in a function, the variable is a global, and lasts until the program completes.
In both languages, once something falls out of scope, it's garbage collected.

Can ABCL's Interpreter load Lisp source from an InputStream?

I've just started looking at ABCL to mix some Lisp into Java. For now, loading some Lisp from a file will be sufficient, and I've been looking at the examples. In every case, the pattern is:
Interpreter interpreter = Interpreter.createInstance();
interpreter.eval("(load \"lispfunctions.lisp\")");
But say I'm building a Maven project with a view to packaging as a JAR: how can I load lispfunctions.lisp from src/main/resources? I can easily get an InputStream—can I go somewhere with that? Or is there another idiom I'm missing here for loading Lisp source from a resource like this?
I've gotten the following to work. I am working with ABCL 1.7.0 on MacOS, although I'm pretty sure this isn't version-specific.
/* load_lisp_within_jar.java -- use ABCL to load Lisp file as resource in jar
* copyright 2020 by Robert Dodier
* I release this work under terms of the GNU General Public License
*/
/* To run this example:
$ javac -cp /path/to/abcl.jar -d . load_lisp_within_jar.java
$ cat << EOF > foo.lisp
(defun f (x) (1+ x))
EOF
$ jar cvf load_lisp_within_jar.jar load_lisp_within_jar.class foo.lisp
$ java -cp load_lisp_within_jar.jar:/path/to/abcl.jar load_lisp_within_jar
*
* Expected output:
(F 100) => 101
*/
import org.armedbear.lisp.*;
import java.io.*;
public class load_lisp_within_jar {
public static void main (String [] args) {
try {
// It appears that interpreter instance is required even though
// it isn't used directly; I guess it arranges global resources.
Interpreter I = Interpreter.createInstance ();
LispObject LOAD_function = Symbol.LOAD.getSymbolFunction ();
// Obtain an input stream for Lisp source code in jar.
ClassLoader L = load_lisp_within_jar.class.getClassLoader ();
InputStream f = L.getResourceAsStream ("foo.lisp");
Stream S = new Stream (Symbol.SYSTEM_STREAM, f, Symbol.CHARACTER);
// Call COMMON-LISP:LOAD with input stream as argument.
LOAD_function.execute (S);
// Verify that function F has been defined.
Symbol F = Packages.findPackage ("COMMON-LISP-USER").findAccessibleSymbol ("F");
LispObject F_function = F.getSymbolFunction ();
LispObject x = F_function.execute (LispInteger.getInstance (100));
System.out.println ("(F 100) => " + x.javaInstance ());
}
catch (Exception e) {
System.err.println ("oops: " + e);
e.printStackTrace ();
}
}
}
As you can see, the program first gets the function associated with the symbol LOAD. (For convenience, many, maybe all of COMMON-LISP symbols have static definitions, so you can just say Symbol.LOAD instead of looking up the symbol via findAccessibleSymbol.) Then the input stream is supplied to the load function. Afterwards we verify that our function F is indeed defined.
I know this stuff can be kind of obscure; I'll be happy to try to answer any questions.

Text based game with a separate timer loop?

I have started a Java coding short course at a university about 5 months ago. I have learnt quite the amount of things with regards to Java coding, but am still lacking in other things such as threads, handling exceptions, or even making JFrame games. I decided to embark on a text based game to just learn and figure out how a game loop should work (kind of), and how the logic should work (still, very "kind of"). The game I wrote runs with if-else commands, thus you get displayed a screen, type in the command of the option you want to pick, and it bumps you to the next menu, very standard of course. I run these if-else statements within nested for loops.
My nested for loops looks like the following:
// This is just an example, they're actually a lot more cluttered
// in my actual source code.
mainMenu.writeOutput();
reply = keyboardInput.nextLine();
for (int i = 0; i <= 10; i--)
{
for (int ii = 0; i <= 10; i--)
{
if (reply.equalsIgnoreCase("/help")
{
System.out.println("Here I have a separate call to a class
file (excuse me, forgot the exact wording), thus it
call something like help.writeOutput(); to display
the help menu");
reply = keyboardInput.nextLine();
if (reply.equalsIgnoreCase("/makegameeasy")
{
// Just an example.
gamedifficultyEasy.writeOutput();
reply = keyboardInput.nextLine();
if (reply.equalsIgnoreCase("/back")
{
mainMenu.writeOutput();
reply = keyboardInput.nextLine();
break;
}
}
else if (reply.equalsIgnoreCase("/makegamedifficult")
{
// Another example.
gamedifficultHard.writeOutput();
reply = keyboardInput.nextLine();
if (reply.equalsIgnoreCase("/back")
{
mainMenu.writeOutput();
reply = keyboardInput.nextLine();
break;
}
}
else if (reply.equalsIgnoreCase("/back")
{
mainMenu.writeOutput();
reply = keyboardInput.nextLine();
break;
}
}
else
{
System.out.println("Here I print out an error for incorrect
input received, standard fare.");
mainMenu.writeOutput();
reply = keyboard.nextLine();
break;
}
}
}
As mentioned, the above is just an example, it's not very elegant, and I can probably use Exceptions for any incorrect info submitted by the user, however I do not know too much of Exceptions to comfortably add them, so I'll do that at a later time, however my main issue at the moment is a part of my game where "resource mining" has to be done on regular intervals. I have been all over Google, but still can't quite catch how to set a Thread or Timer up for my game so it does the mining automatically, and the player can go on with their game.
The game is essentially one of those games where you build up a base, upgrade your mining tools, and generate more "stuff". I have pasted a few blocks of code from my "mining" class file below that will basically run how much of one thing should be mined. In the game you will be able to buy upgrades of course, so it will get factored into your mining speed.
// I initiate my variables a lot earlier, but just for some
// clarity, I have initiated the variables in the below methods,
// they will not work correctly anyway, I am aware of that, however
// I didn't want to add the other "get, set and initiate"
// variables and methods everywhere, as to not spam this block of code.
// upgradeOS, upgradeHF, and upgradeLM all have their own respective
// set and get methods. They are also all int variables.
public void handleOS()
{
// OS = short for Oxygen Silo
int mineOS = os.getStoredO2() + (1 * upgradeOS);
os.setStoredO2(mineOS);
}
public void handleHF()
{
// HF = short for Hydrogen Fuel
int mineHF = hf.getStoredO2() + (1 * upgradeHF);
hf.setStoredO2(mineHF);
}
public void handleLM()
{
// LM = short for Liquid Minerals
int mineLM = lm.getStoredMinerals() + (1 * upgradeLM);
lm.setStoredMinerals(mineLM);
}
// This is what's going to run the whole time on the set intervals.
public void mine()
{
mineOS = os.getStoredO2() + (1 * upgradeOS);
mineHF = hf.getStoredO2() + (1 * upgradeHF);
mineLM = lm.getStoredMinerals() + (1 * upgradeLM);
os.setStoredO2(mineOS);
hf.setStoredO2(mineHF);
lm.setStoredMinerals(mineLM);
}
// Using 10 seconds here to have it update as quickly as possible so I can
// see any changes. This is just here to write an output.
public void getUpgradeInfo()
{
System.out.println("Oxygen: " + (1 * upgradeOS) + " / per 10 seconds.");
System.out.println("Hydrogen: " + (1 * upgradeHF) + " / per 10 seconds.");
System.out.println("Liquid Mineral: " + (1 * upgradeLM) + " / per 10 seconds.");
}
I'm not the best naming schemes for my materials...
TL;DR: I can't figure out how to implement a thread or timer just for the above mentioned mine() method since I do not have the appropriate amount of knowledge. My if-else rules aren't too elegant, but I'll work on those of course. Basically the if-else rules should run separately from the mine() method, and you can do some AFKing without the game updating the System.out output, thus you can be floating in, for example, the Oxygen Silo upgrade menu, and you won't be bounced back to a different menu due to a thread "waking up", such as being bounced to the main menu, but the mine() method will still generate resources in the background as it should.
Any help on this, or just even a nudge in the right direction will be greatly appreciated.
To answer the question you asked, you can do something like this:
import java.util.*;
TimerTask tt = new TimerTask() {
public void run() {
mine();
}
}
Timer t = new Timer();
t.scheduleAtFixedRate(tt, 0, 1000);
Alternatively, you can use an ActionListener and the swing timer in a similar way. This has the advantage of being Thread-safe in case you build a swing gui on top
Lastly, you should check out the usage of synchronized and volatile to make sure that the variable(s) that are updated in mine() are done so in a thread-safe way
Thanks to #ControlAltDel, definite shove in the right direction. I have taken a bit of code and set it up like this:
import java.util.*;
// extends TimerTask needed
public class TimerTestOne extends TimerTask
{
// Needed
#Override
public void run()
{
TimerTestTwo ttt = new TimerTestTwo();
mine();
}
// Needed, method doesn't need the same name though.
private void completeTask()
{
try
{
//assuming it takes 10 secs to complete the task
Thread.sleep(10000);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
public static void main(String[] args)
{
// You will need to define the following line of code:
TimerTask tt = new TimerTestOne();
Scanner keyboard = new Scanner(System.in);
String reply;
// Following 2 lines of code, yup, need them.
Timer timer = new Timer(true);
timer.scheduleAtFixedRate(tt, 0, 10*1000);
previousMenu();
for (int i = 0; i <= 10000; i++)
{
for (int ii = 0; ii <= 10000; i++)
{
System.out.println("Go to another menu?");
reply = keyboard.nextLine();
if (reply.equalsIgnoreCase("/yes"))
{
yes();
reply = keyboard.nextLine();
}
}
}
}
// I added the following methods, just so I didn't have to work
// off 2 class files.
public void mine()
{
System.out.println("Mined");
}
public static void yes()
{
System.out.println("Next menu");
}
public static void previousMenu()
{
System.out.println("Previous menu");
}
}
So there, if anyone ever needs to have a look at setting a timer up that won't break your text based game.

Getting the results of a Haskell script from Java

I'm trying to create a program to compare the amount of time it takes various haskell scripts to run, which will later be used to create graphs and displayed in a GUI. I've tried to create said GUI using Haskell libraries but I haven't had much luck, especially since I'm having trouble finding up to date GUI libraries for Windows. I've tried to use Java to get these results but either get errors returned or simply no result.
I've constructed a minimal example to show roughly what I'm doing at the moment:
import java.io.*;
public class TestExec {
public static void main(String[] args) {
try {
Process p = Runtime.getRuntime().exec("ghc test.hs 2 2");
BufferedReader in = new BufferedReader(
new InputStreamReader(p.getInputStream()));
String line = null;
while ((line = in.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
And here is the Haskell script this is calling, in this case a simple addition:
test x y = x + y
Currently there simply isn't any result stored or printed. Anyone have any ideas?
Since you're attempting to run this as an executable, you need to provide a main. In you're case it should look something like
import System.Environment
test :: Integer -> Integer -> Integer
test = (+)
main = do
[x, y] <- map read `fmap` getArgs
print $ x `test` y
This just reads the command line arguments, adds them, then prints them. Though I did something like a while ago, it's much easier to do the benchmarking/testing in Haskell, and dump the output data to a text file in a more structured format, then parse/display it in Java or whatever language you like.
This is mostly a Java question. Search for Runtime.getRuntime().exec().
On the Haskell side, you need to write a stand-alone Haskell script. The one by #jozefg is OK. You should be able to run it as
runghc /path/to/script.hs 1 2
from the command line.
Calling it from Java is no different than running any other external process in Java. In Clojure (a JVM language, I use it for brevity) I do:
user=> (def p (-> (Runtime/getRuntime) (.exec "/usr/bin/runghc /tmp/test.hs 1 2")))
#'user/p
user=> (-> p .getInputStream input-stream reader line-seq)
("3")
Please note that I use runghc to run a script (not ghc). Full paths are not necessary, but could be helpful. Your Java program can be modified this way:
--- TestExec.question.java
+++ TestExec.java
## -2,7 +2,7 ##
public class TestExec {
public static void main(String[] args) {
try {
- Process p = Runtime.getRuntime().exec("ghc test.hs 2 2");
+ Process p = Runtime.getRuntime().exec("/usr/bin/runghc /tmp/test.hs 2 2");
BufferedReader in = new BufferedReader(
new InputStreamReader(p.getInputStream()));
String line = null;
The modified version runs the Haskell script just fine. You may have to change paths to you runghc and test.hs locations.
At first to read from output you need to use OutputStreamReader(p.getOutputStream()) instead of InputStreamReader
As I said in comment such a benchmark is simply incorrect. While benchmarking one should eliminate as many side coasts as possible. The best solution is to use the criterion package. It produces nice graphical output as you desire.
Small example:
import Criterion
import Criterion.Main
import Criterion.Config
fac 1 = 1
fac n = n * (fac $ n-1)
myConfig = defaultConfig {
cfgReport = ljust "report.html"
}
main = defaultMainWith myConfig (return ()) [
bench "fac 30" $ whnf fac 30
]
After execution it produces a file "report.html" with neat interactive plots.

Java exercise for checkout

I'm trying some Java recently and look for some review of my style. If You like to look at this exercise placed in the image, and tell me if my style is good enought? Or maybe it is not good enought, so You can tell me on what aspect I should work more, so You can help me to improve it?
exercise for my question
/*
* File: MathQuiz.java
*
* This program produces Math Quiz.
*/
import acm.program.*;
import acm.util.*;
public class MathQuiz extends ConsoleProgram {
/* Class constants for Quiz settings. */
private static final int CHANCES = 3;
private static final int QUESTIONS = 5;
private static final int MIN = 0;
private static final int MAX = 20;
/* Start program. Number of questions to ask is assigned here. */
public void run() {
println("Welcome to Math Quiz");
while(answered != QUESTIONS) {
produceNumbers();
askForAnswer();
}
println("End of program.");
}
/* Ask for answer, and check them. Number of chances includes
* first one, where user is asked for reply. */
private void askForAnswer() {
int answer = -1;
if(type)
answer = readInt("What is " + x + "+" + y + "?");
else
answer = readInt("What is " + x + "-" + y + "?");
for(int i = 1; i < CHANCES+1; i++) {
if(answer != solution) {
if(i == CHANCES) {
println("No. The answer is " + solution + ".");
break;
}
answer = readInt("That's incorrect - try a different answer: ");
} else {
println("That's the answer!");
break;
}
}
answered++;
}
/* Produces type and two numbers until they qualify. */
private void produceNumbers() {
produceType();
produceFirst();
produceSecond();
if(type)
while(x+y >= MAX) {
produceFirst();
produceSecond();
}
else
while(x-y <= MIN) {
produceFirst();
produceSecond();
}
calculateSolution();
}
/* Calculates equation solution. */
private void calculateSolution() {
if(type) solution = x + y;
else solution = x - y;
}
/* Type of the equation. True is from plus, false is for minus. */
private void produceType() {
type = rgen.nextBoolean();
}
/* Produces first number. */
private void produceFirst() {
x = rgen.nextInt(0, 20);
}
/* Produces second number. */
private void produceSecond() {
y = rgen.nextInt(0, 20);
}
/* Class variables for numbers and type of the equation. */
private static boolean type;
private static int x;
private static int y;
/* Class variables for equation solution. */
private static int solution;
/* Class variable counting number of answered equations,
* so if it reaches number of provided questions, it ends */
private static int answered = 0;
/* Random generator constructor. */
RandomGenerator rgen = new RandomGenerator();
}
One thing I noticed was that all of your methods take no parameters and return void.
I think it would be clearer if you use method parameters and return values to show the flow of data through your program instead of using the object's state to store everything.
There are a few things you should do differently, and a couple you could do differently.
The things you should do differently:
Keep all fields together.
static fields should always be in THIS_FORM
you've used the static modifier for what clearly look like instance fields. (type,x,y,solution, answered). This means you can only ever run one MathsQuiz at a time per JVM. Not a big deal in this case, but will cause problems for more complex programs.
produceFirst and produceSecond use hardcoded parameters to nextInt rather than using MAX and MIN as provided by the class
There is no apparent need for answered to be a field. It could easily be a local variable in run.
Things you should do differently:
There is a small possibility (however tiny), that produceNumbers might not end. Instead of producing two random numbers and hoping they work. Produce one random number and then constrain the second so that a solution will always be formed. eg. say we are doing and addition and x is 6 and max is 20. We know that y cannot be larger than 14. So instead of trying nextInt(0,20), you could do nextInt(0,14) and be assured that you would get a feasible question.
For loop isn't really the right construct for askForAnswer as the desired behaviour is to ask for an answer CHANCES number of times or until a correct answer is received, whichever comes first. A for loop is usually used when you wish to do something a set number of times. Indeed the while loop in run is a good candidate for a for loop. A sample while loop might look like:
int i = 1;
boolean correct = (solution == readInt("What is " + x + "+" + y + "?"));
while (i < CHANCES && !correct) {
correct = (solution == readInt("Wrong, try again."));
i++;
}
if (correct) {
println("Well done!");
} else {
println("Nope, the answer is: "+solution);
}
Looks like a very clean program style. I would move all variables to the top instead of having some at the bottom, but other than that it is very readable.
Here is something I'd improve: the boolean type that is used to indicate whether we have an addition or subtraction:
private void produceType() {
type = rgen.nextBoolean();
}
produceType tells, that something is generated and I'd expect something to be returned. And I'd define enums to represent the type of the quiz. Here's my suggestion:
private QuizType produceType() {
boolean type = rgen.nextBoolean();
if (type == true)
return QuizType.PLUS;
else
return QuizType.MINUS;
}
The enum is defined like this:
public enum QuizType { PLUS, MINUS }
Almost good I have only a few improvements:
variables moves to the top
Inside produceNumbers and your while you have small repeat. I recommend refactor this
Small advice: Code should be like books - easy readable - in your run() method firstly you call produceNumber and then askForAnswer. So it will be better if in your code you will have the same order in definitions, so implementation askForAnswer before produceNumber. But it isn't necessary
Pay attention to have small methods. A method shouldn't have much to do - I think that askForAnswer you could split to two methods

Categories

Resources