From 801320e77d9e3670b4bc162ca7b65dfcca14da53 Mon Sep 17 00:00:00 2001 From: Victor Date: Thu, 15 Nov 2018 17:01:32 +0200 Subject: [PATCH] Initial --- build.xml | 73 + nbproject/build-impl.xml | 1419 +++++++++++++++++ nbproject/genfiles.properties | 8 + nbproject/private/config.properties | 0 nbproject/private/private.properties | 7 + nbproject/private/private.xml | 7 + nbproject/project.properties | 76 + nbproject/project.xml | 15 + paint_lines.txt | 18 + program.txt | 17 + src/com/annimon/automatetool/Main.java | 69 + src/com/annimon/automatetool/RobotUtils.java | 124 ++ .../automatetool/lib/BooleanValue.java | 37 + .../automatetool/lib/IntegerValue.java | 37 + .../annimon/automatetool/lib/StringValue.java | 42 + src/com/annimon/automatetool/lib/Value.java | 14 + .../annimon/automatetool/lib/Variables.java | 35 + .../annimon/automatetool/parser/Lexer.java | 148 ++ .../annimon/automatetool/parser/Parser.java | 234 +++ .../annimon/automatetool/parser/Token.java | 33 + .../automatetool/parser/TokenType.java | 42 + .../parser/ast/AssignmentStatement.java | 28 + .../automatetool/parser/ast/AstHelper.java | 49 + .../parser/ast/BinaryExpression.java | 60 + .../parser/ast/BlockStatement.java | 40 + .../parser/ast/ClickStatement.java | 26 + .../parser/ast/ConditionalExpression.java | 54 + .../parser/ast/DelayStatement.java | 26 + .../automatetool/parser/ast/Expression.java | 12 + .../automatetool/parser/ast/ForStatement.java | 31 + .../parser/ast/KeyPressStatement.java | 26 + .../parser/ast/KeyReleaseStatement.java | 26 + .../parser/ast/MoveStatement.java | 30 + .../annimon/automatetool/parser/ast/Node.java | 9 + .../parser/ast/PressStatement.java | 26 + .../parser/ast/ReleaseStatement.java | 26 + .../automatetool/parser/ast/Statement.java | 10 + .../parser/ast/TypeStatement.java | 26 + .../parser/ast/ValueExpression.java | 41 + .../parser/ast/VariableExpression.java | 27 + 40 files changed, 3028 insertions(+) create mode 100644 build.xml create mode 100644 nbproject/build-impl.xml create mode 100644 nbproject/genfiles.properties create mode 100644 nbproject/private/config.properties create mode 100644 nbproject/private/private.properties create mode 100644 nbproject/private/private.xml create mode 100644 nbproject/project.properties create mode 100644 nbproject/project.xml create mode 100644 paint_lines.txt create mode 100644 program.txt create mode 100644 src/com/annimon/automatetool/Main.java create mode 100644 src/com/annimon/automatetool/RobotUtils.java create mode 100644 src/com/annimon/automatetool/lib/BooleanValue.java create mode 100644 src/com/annimon/automatetool/lib/IntegerValue.java create mode 100644 src/com/annimon/automatetool/lib/StringValue.java create mode 100644 src/com/annimon/automatetool/lib/Value.java create mode 100644 src/com/annimon/automatetool/lib/Variables.java create mode 100644 src/com/annimon/automatetool/parser/Lexer.java create mode 100644 src/com/annimon/automatetool/parser/Parser.java create mode 100644 src/com/annimon/automatetool/parser/Token.java create mode 100644 src/com/annimon/automatetool/parser/TokenType.java create mode 100644 src/com/annimon/automatetool/parser/ast/AssignmentStatement.java create mode 100644 src/com/annimon/automatetool/parser/ast/AstHelper.java create mode 100644 src/com/annimon/automatetool/parser/ast/BinaryExpression.java create mode 100644 src/com/annimon/automatetool/parser/ast/BlockStatement.java create mode 100644 src/com/annimon/automatetool/parser/ast/ClickStatement.java create mode 100644 src/com/annimon/automatetool/parser/ast/ConditionalExpression.java create mode 100644 src/com/annimon/automatetool/parser/ast/DelayStatement.java create mode 100644 src/com/annimon/automatetool/parser/ast/Expression.java create mode 100644 src/com/annimon/automatetool/parser/ast/ForStatement.java create mode 100644 src/com/annimon/automatetool/parser/ast/KeyPressStatement.java create mode 100644 src/com/annimon/automatetool/parser/ast/KeyReleaseStatement.java create mode 100644 src/com/annimon/automatetool/parser/ast/MoveStatement.java create mode 100644 src/com/annimon/automatetool/parser/ast/Node.java create mode 100644 src/com/annimon/automatetool/parser/ast/PressStatement.java create mode 100644 src/com/annimon/automatetool/parser/ast/ReleaseStatement.java create mode 100644 src/com/annimon/automatetool/parser/ast/Statement.java create mode 100644 src/com/annimon/automatetool/parser/ast/TypeStatement.java create mode 100644 src/com/annimon/automatetool/parser/ast/ValueExpression.java create mode 100644 src/com/annimon/automatetool/parser/ast/VariableExpression.java diff --git a/build.xml b/build.xml new file mode 100644 index 0000000..ff1dc4b --- /dev/null +++ b/build.xml @@ -0,0 +1,73 @@ + + + + + + + + + + + Builds, tests, and runs the project AutomateTool. + + + diff --git a/nbproject/build-impl.xml b/nbproject/build-impl.xml new file mode 100644 index 0000000..270c721 --- /dev/null +++ b/nbproject/build-impl.xml @@ -0,0 +1,1419 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set src.dir + Must set test.src.dir + Must set build.dir + Must set dist.dir + Must set build.classes.dir + Must set dist.javadoc.dir + Must set build.test.classes.dir + Must set build.test.results.dir + Must set build.classes.excludes + Must set dist.jar + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set javac.includes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + No tests executed. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set JVM to use for profiling in profiler.info.jvm + Must set profiler agent JVM arguments in profiler.info.jvmargs.agent + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select some files in the IDE or set javac.includes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + To run this application from the command line without Ant, try: + + java -jar "${dist.jar.resolved}" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set run.class + + + + Must select one file in the IDE or set run.class + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set debug.class + + + + + Must select one file in the IDE or set debug.class + + + + + Must set fix.includes + + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + Must select one file in the IDE or set profile.class + This target only works when run from inside the NetBeans IDE. + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set run.class + + + + + + Must select some files in the IDE or set test.includes + + + + + Must select one file in the IDE or set run.class + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select some files in the IDE or set javac.includes + + + + + + + + + + + + + + + + + + + + Some tests failed; see details above. + + + + + + + + + Must select some files in the IDE or set test.includes + + + + Some tests failed; see details above. + + + + Must select some files in the IDE or set test.class + Must select some method in the IDE or set test.method + + + + Some tests failed; see details above. + + + + + Must select one file in the IDE or set test.class + + + + Must select one file in the IDE or set test.class + Must select some method in the IDE or set test.method + + + + + + + + + + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/nbproject/genfiles.properties b/nbproject/genfiles.properties new file mode 100644 index 0000000..33ee99f --- /dev/null +++ b/nbproject/genfiles.properties @@ -0,0 +1,8 @@ +build.xml.data.CRC32=67bd077b +build.xml.script.CRC32=2f28d9f7 +build.xml.stylesheet.CRC32=8064a381@1.79.0.48 +# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml. +# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you. +nbproject/build-impl.xml.data.CRC32=67bd077b +nbproject/build-impl.xml.script.CRC32=fa865cff +nbproject/build-impl.xml.stylesheet.CRC32=05530350@1.79.0.48 diff --git a/nbproject/private/config.properties b/nbproject/private/config.properties new file mode 100644 index 0000000..e69de29 diff --git a/nbproject/private/private.properties b/nbproject/private/private.properties new file mode 100644 index 0000000..03c76f4 --- /dev/null +++ b/nbproject/private/private.properties @@ -0,0 +1,7 @@ +application.args=paint_lines.txt +compile.on.save=true +do.depend=false +do.jar=true +javac.debug=true +javadoc.preview=true +user.properties.file=C:\\Users\\aNNiMON\\AppData\\Roaming\\NetBeans\\dev\\build.properties diff --git a/nbproject/private/private.xml b/nbproject/private/private.xml new file mode 100644 index 0000000..e74a594 --- /dev/null +++ b/nbproject/private/private.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/nbproject/project.properties b/nbproject/project.properties new file mode 100644 index 0000000..acdc97b --- /dev/null +++ b/nbproject/project.properties @@ -0,0 +1,76 @@ +annotation.processing.enabled=true +annotation.processing.enabled.in.editor=false +annotation.processing.processors.list= +annotation.processing.run.all.processors=true +annotation.processing.source.output=${build.generated.sources.dir}/ap-source-output +application.title=AutomateTool +application.vendor=aNNiMON +build.classes.dir=${build.dir}/classes +build.classes.excludes=**/*.java,**/*.form +# This directory is removed when the project is cleaned: +build.dir=build +build.generated.dir=${build.dir}/generated +build.generated.sources.dir=${build.dir}/generated-sources +# Only compile against the classpath explicitly listed here: +build.sysclasspath=ignore +build.test.classes.dir=${build.dir}/test/classes +build.test.results.dir=${build.dir}/test/results +# Uncomment to specify the preferred debugger connection transport: +#debug.transport=dt_socket +debug.classpath=\ + ${run.classpath} +debug.test.classpath=\ + ${run.test.classpath} +# Files in build.classes.dir which should be excluded from distribution jar +dist.archive.excludes= +# This directory is removed when the project is cleaned: +dist.dir=dist +dist.jar=${dist.dir}/AutomateTool.jar +dist.javadoc.dir=${dist.dir}/javadoc +endorsed.classpath= +excludes= +includes=** +jar.compress=false +javac.classpath= +# Space-separated list of extra javac options +javac.compilerargs= +javac.deprecation=false +javac.external.vm=true +javac.processorpath=\ + ${javac.classpath} +javac.source=1.8 +javac.target=1.8 +javac.test.classpath=\ + ${javac.classpath}:\ + ${build.classes.dir} +javac.test.processorpath=\ + ${javac.test.classpath} +javadoc.additionalparam= +javadoc.author=false +javadoc.encoding=${source.encoding} +javadoc.noindex=false +javadoc.nonavbar=false +javadoc.notree=false +javadoc.private=false +javadoc.splitindex=true +javadoc.use=true +javadoc.version=false +javadoc.windowtitle= +main.class=com.annimon.automatetool.Main +manifest.file=manifest.mf +meta.inf.dir=${src.dir}/META-INF +mkdist.disabled=false +platform.active=default_platform +run.classpath=\ + ${javac.classpath}:\ + ${build.classes.dir} +# Space-separated list of JVM arguments used when running the project. +# You may also define separate properties like run-sys-prop.name=value instead of -Dname=value. +# To set system properties for unit tests define test-sys-prop.name=value: +run.jvmargs= +run.test.classpath=\ + ${javac.test.classpath}:\ + ${build.test.classes.dir} +source.encoding=UTF-8 +src.dir=src +test.src.dir=test diff --git a/nbproject/project.xml b/nbproject/project.xml new file mode 100644 index 0000000..71eb08f --- /dev/null +++ b/nbproject/project.xml @@ -0,0 +1,15 @@ + + + org.netbeans.modules.java.j2seproject + + + AutomateTool + + + + + + + + + diff --git a/paint_lines.txt b/paint_lines.txt new file mode 100644 index 0000000..1cc7d47 --- /dev/null +++ b/paint_lines.txt @@ -0,0 +1,18 @@ +pause = 5 +xstep = 50 ystep = 5 +startx = 50 +starty = 400 + ystep / 2 + +for (y = 0, y < 300, y = y + ystep) { + + move startx (starty+y) + press BUTTON1 + for (i = 0, i < 600, i = i + xstep) { + move (startx+i) (starty+y) + delay pause + } + release BUTTON1 + delay (pause*3) + +} + diff --git a/program.txt b/program.txt new file mode 100644 index 0000000..ac962f1 --- /dev/null +++ b/program.txt @@ -0,0 +1,17 @@ +# double click to create tab +move 1256 400 +click BUTTON1 +delay 5 +click BUTTON1 + +# focus on editor +move 1256 460 +click BUTTON1 + +# type text +text = "This is a text. Enjoy. + +Multiline text also supported. + +Thank you for whatching my stream :)" +type text \ No newline at end of file diff --git a/src/com/annimon/automatetool/Main.java b/src/com/annimon/automatetool/Main.java new file mode 100644 index 0000000..385d570 --- /dev/null +++ b/src/com/annimon/automatetool/Main.java @@ -0,0 +1,69 @@ +package com.annimon.automatetool; + +import com.annimon.automatetool.parser.Lexer; +import com.annimon.automatetool.parser.Parser; +import com.annimon.automatetool.parser.Token; +import com.annimon.automatetool.parser.ast.Statement; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.List; + +/** + * @author aNNiMON + */ +public final class Main { + + public static void main(String[] args) { + final String source = readSource(args); + System.out.println("-= PROGRAM =-"); + System.out.println(source); + + final Lexer lexer = new Lexer(source); + final List tokens = lexer.tokenize(); + System.out.println("\n\n-= TOKENS =-"); + System.out.println(tokens); + + RobotUtils.initialize(); + final Parser parser = new Parser(tokens); + final Statement program = parser.parse(); + program.execute(); + System.out.println("\n\n-= AST =-"); + System.out.println(program); + } + + private static String readSource(String[] args) { + String source; + if (args.length >= 1) { + final File input = new File(args[0]); + if (input.exists() && input.isFile()) { + source = read(input); + } else { + source = args[0]; + } + } else { + source = "move 200 200\n" + + "delay 100"; + } + return source; + } + + private static String read(File file) { + final String newLine = System.getProperty("line.separator"); + final StringBuilder sb = new StringBuilder(); + try ( final InputStream is = new FileInputStream(file); + final InputStreamReader isReader = new InputStreamReader(is, "UTF-8"); + final BufferedReader reader = new BufferedReader(isReader) ) { + String line; + while ( (line = reader.readLine()) != null) { + sb.append(line); + sb.append(newLine); + } + } catch (IOException ex) { + } + return sb.toString(); + } +} diff --git a/src/com/annimon/automatetool/RobotUtils.java b/src/com/annimon/automatetool/RobotUtils.java new file mode 100644 index 0000000..4a017db --- /dev/null +++ b/src/com/annimon/automatetool/RobotUtils.java @@ -0,0 +1,124 @@ +package com.annimon.automatetool; + +import java.awt.AWTException; +import java.awt.Robot; +import java.awt.event.KeyEvent; +import java.util.HashMap; +import java.util.Map; + +/** + * + * @author aNNiMON + */ +public final class RobotUtils { + + private static final int CLICK_DELAY = 200; + private static final int TYPING_DELAY = 50; + + private static final Map SYMBOL_CODES; + static { + SYMBOL_CODES = new HashMap<>(10); + SYMBOL_CODES.put('_', KeyEvent.VK_MINUS); + SYMBOL_CODES.put(':', KeyEvent.VK_SEMICOLON); + } + + private static Robot robot; + + public static void initialize() { + try { + robot = new Robot(); + } catch (AWTException awte) { + throw new RuntimeException("Unable to create robot instance", awte); + } + } + + public static synchronized void click(int buttons) { + mousePress(buttons); + delay(CLICK_DELAY); + mouseRelease(buttons); + } + + public static synchronized void typeText(String text) { + for (char ch : text.toCharArray()) { + typeCharacter(ch); + } + } + + private static void typeCharacter(char ch) { + int code = KeyEvent.getExtendedKeyCodeForChar(ch); + + boolean isUpperCase = Character.isLetter(ch) && Character.isUpperCase(ch); + boolean needPressShift = isUpperCase; + if (!isUpperCase) { + final int symbolIndex = "!@#$%^&*()".indexOf(ch); + if (symbolIndex != -1) { + needPressShift = true; + code = '1' + symbolIndex; + } else if (SYMBOL_CODES.containsKey(ch)) { + needPressShift = true; + code = SYMBOL_CODES.get(ch); + } + } + + if (code == KeyEvent.VK_UNDEFINED) return; + + if (needPressShift) { + // press shift + keyPress(KeyEvent.VK_SHIFT); + delay(TYPING_DELAY); + } + + keyPress(code); + delay(TYPING_DELAY); + keyRelease(code); + + if (needPressShift) { + // release shift + delay(TYPING_DELAY); + keyRelease(KeyEvent.VK_SHIFT); + delay(TYPING_DELAY); + } + } + + public static synchronized void mouseMove(int x, int y) { + try { + robot.mouseMove(x, y); + } catch (IllegalArgumentException iae) { } + } + + public static synchronized void mousePress(int buttons) { + try { + robot.mousePress(buttons); + } catch (IllegalArgumentException iae) { } + } + + public static synchronized void mouseRelease(int buttons) { + try { + robot.mouseRelease(buttons); + } catch (IllegalArgumentException iae) { } + } + + public static synchronized void mouseWheel(int wheelAmt) { + try { + robot.mouseWheel(wheelAmt); + } catch (IllegalArgumentException iae) { } + } + + public static synchronized void keyPress(int keycode) { + try { + robot.keyPress(keycode); + } catch (IllegalArgumentException iae) { } + } + + public static synchronized void keyRelease(int keycode) { + try { + robot.keyRelease(keycode); + } catch (IllegalArgumentException iae) { } + } + + public static synchronized void delay(int ms) { + try { + robot.delay(ms); + } catch (IllegalArgumentException iae) { } + } +} diff --git a/src/com/annimon/automatetool/lib/BooleanValue.java b/src/com/annimon/automatetool/lib/BooleanValue.java new file mode 100644 index 0000000..521b394 --- /dev/null +++ b/src/com/annimon/automatetool/lib/BooleanValue.java @@ -0,0 +1,37 @@ +package com.annimon.automatetool.lib; + +/** + * + * @author aNNiMON + */ +public final class BooleanValue implements Value { + + public static final BooleanValue FALSE = new BooleanValue(false); + public static final BooleanValue TRUE = new BooleanValue(true); + + private final boolean value; + + public BooleanValue(boolean value) { + this.value = value; + } + + @Override + public boolean asBoolean() { + return value; + } + + @Override + public int asInteger() { + return value ? 1 : 0; + } + + @Override + public String asString() { + return String.valueOf(value); + } + + @Override + public String toString() { + return "boolean{" + value + '}'; + } +} diff --git a/src/com/annimon/automatetool/lib/IntegerValue.java b/src/com/annimon/automatetool/lib/IntegerValue.java new file mode 100644 index 0000000..57a8477 --- /dev/null +++ b/src/com/annimon/automatetool/lib/IntegerValue.java @@ -0,0 +1,37 @@ +package com.annimon.automatetool.lib; + +/** + * + * @author aNNiMON + */ +public final class IntegerValue implements Value { + + public static final IntegerValue ZERO = new IntegerValue(0); + public static final IntegerValue ONE = new IntegerValue(1); + + private final int value; + + public IntegerValue(int value) { + this.value = value; + } + + @Override + public boolean asBoolean() { + return value != 0; + } + + @Override + public int asInteger() { + return value; + } + + @Override + public String asString() { + return String.valueOf(value); + } + + @Override + public String toString() { + return "int{" + value + '}'; + } +} diff --git a/src/com/annimon/automatetool/lib/StringValue.java b/src/com/annimon/automatetool/lib/StringValue.java new file mode 100644 index 0000000..c699f47 --- /dev/null +++ b/src/com/annimon/automatetool/lib/StringValue.java @@ -0,0 +1,42 @@ +package com.annimon.automatetool.lib; + +/** + * + * @author aNNiMON + */ +public final class StringValue implements Value { + + private final String value; + + public StringValue(String value) { + this.value = value; + } + + @Override + public boolean asBoolean() { + try { + return Boolean.parseBoolean(value); + } catch (NumberFormatException nfe) { + throw new RuntimeException("Cannot cast string value to boolean"); + } + } + + @Override + public int asInteger() { + try { + return Integer.parseInt(value); + } catch (NumberFormatException nfe) { + throw new RuntimeException("Cannot cast string value to integer"); + } + } + + @Override + public String asString() { + return value; + } + + @Override + public String toString() { + return value; + } +} diff --git a/src/com/annimon/automatetool/lib/Value.java b/src/com/annimon/automatetool/lib/Value.java new file mode 100644 index 0000000..3a31dbe --- /dev/null +++ b/src/com/annimon/automatetool/lib/Value.java @@ -0,0 +1,14 @@ +package com.annimon.automatetool.lib; + +/** + * + * @author aNNiMON + */ +public interface Value { + + boolean asBoolean(); + + int asInteger(); + + String asString(); +} diff --git a/src/com/annimon/automatetool/lib/Variables.java b/src/com/annimon/automatetool/lib/Variables.java new file mode 100644 index 0000000..e3b6b8c --- /dev/null +++ b/src/com/annimon/automatetool/lib/Variables.java @@ -0,0 +1,35 @@ +package com.annimon.automatetool.lib; + +import java.awt.event.InputEvent; +import java.util.HashMap; +import java.util.Map; + +/** + * + * @author aNNiMON + */ +public final class Variables { + + private static final Map variables; + static { + variables = new HashMap<>(); + // TODO: TrueExpression, FalseExpression + variables.put("false", BooleanValue.FALSE); + variables.put("true", BooleanValue.TRUE); + + variables.put("BUTTON1", new IntegerValue(InputEvent.BUTTON1_MASK)); + variables.put("BUTTON2", new IntegerValue(InputEvent.BUTTON2_MASK)); + variables.put("BUTTON3", new IntegerValue(InputEvent.BUTTON3_MASK)); + } + + public static Value get(String variable) { + if (!variables.containsKey(variable)) { + throw new RuntimeException("Variable " + variable + " does not exist"); + } + return variables.get(variable); + } + + public static void set(String variable, Value value) { + variables.put(variable, value); + } +} diff --git a/src/com/annimon/automatetool/parser/Lexer.java b/src/com/annimon/automatetool/parser/Lexer.java new file mode 100644 index 0000000..fc51876 --- /dev/null +++ b/src/com/annimon/automatetool/parser/Lexer.java @@ -0,0 +1,148 @@ +package com.annimon.automatetool.parser; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * + * @author aNNiMON + */ +public final class Lexer { + + private static final Map OPERATORS; + static { + OPERATORS = new HashMap<>(10); + OPERATORS.put("(", TokenType.LPAREN); + OPERATORS.put(")", TokenType.RPAREN); + OPERATORS.put(",", TokenType.COMMA); + OPERATORS.put("{", TokenType.LBRACE); + OPERATORS.put("}", TokenType.RBRACE); + OPERATORS.put("=", TokenType.EQ); + OPERATORS.put("<", TokenType.LT); + OPERATORS.put(">", TokenType.GT); + OPERATORS.put("+", TokenType.PLUS); + OPERATORS.put("-", TokenType.MINUS); + OPERATORS.put("*", TokenType.STAR); + OPERATORS.put("/", TokenType.SLASH); + } + + private static final Map KEYWORDS; + static { + KEYWORDS = new HashMap<>(10); + KEYWORDS.put("move", TokenType.MOVE); + KEYWORDS.put("click", TokenType.CLICK); + KEYWORDS.put("delay", TokenType.DELAY); + KEYWORDS.put("press", TokenType.PRESS); + KEYWORDS.put("release", TokenType.RELEASE); + KEYWORDS.put("keypress", TokenType.KEYPRESS); + KEYWORDS.put("keyrelease", TokenType.KEYRELEASE); + KEYWORDS.put("type", TokenType.TYPE); + + KEYWORDS.put("for", TokenType.FOR); + } + + private final int length; + private final String source; + + private final List tokens; + + private int pos; + + public Lexer(String source) { + this.source = source; + this.length = source.length(); + tokens = new ArrayList<>(); + pos = 0; + } + + public List tokenize() { + while (pos < length) { + final char current = peek(0); + if (Character.isDigit(current)) tokenizeNumber(); + else if (Character.isLetter(current)) tokenizeWord(); + else if (current == '"') tokenizeText(); + else if (current == '#') tokenizeComment(); + else if (OPERATORS.containsKey(String.valueOf(current))) { + tokenizeOperator(); + } else next(); + } + + return tokens; + } + + private void tokenizeNumber() { + final StringBuilder sb = new StringBuilder(); + char current = peek(0); + while (Character.isDigit(current)) { + sb.append(current); + current = next(); + } + addToken(TokenType.NUMBER, sb.toString()); + } + + private void tokenizeWord() { + final StringBuilder sb = new StringBuilder(); + char current = peek(0); + while (Character.isLetterOrDigit(current)) { + sb.append(current); + current = next(); + } + final String word = sb.toString(); + if (KEYWORDS.containsKey(word.toLowerCase())) { + addToken( KEYWORDS.get(word.toLowerCase()) ); + } else { + addToken(TokenType.WORD, word); + } + } + + private void tokenizeText() { + next(); + final StringBuilder sb = new StringBuilder(); + char current = peek(0); + while (current != '"') { + if (current == '\0') { + throw new RuntimeException("unexpected end of file"); + } + sb.append(current); + current = next(); + } + next(); + addToken(TokenType.TEXT, sb.toString()); + } + + private void tokenizeComment() { + char current = peek(0); + while ("\r\n\0".indexOf(current) == -1) { + current = next(); + } + } + + private void tokenizeOperator() { + char current = peek(0); + addToken(OPERATORS.get(String.valueOf(current))); + next(); + } + + private char next() { + pos++; + return peek(0); + } + + private char peek(int relativePosition) { + final int position = pos + relativePosition; + if (position >= length) { + return '\0'; + } + return source.charAt(position); + } + + private void addToken(TokenType type) { + tokens.add(new Token(type, "")); + } + + private void addToken(TokenType type, String text) { + tokens.add(new Token(type, text)); + } +} diff --git a/src/com/annimon/automatetool/parser/Parser.java b/src/com/annimon/automatetool/parser/Parser.java new file mode 100644 index 0000000..507ecb6 --- /dev/null +++ b/src/com/annimon/automatetool/parser/Parser.java @@ -0,0 +1,234 @@ +package com.annimon.automatetool.parser; + +import com.annimon.automatetool.parser.ast.AssignmentStatement; +import com.annimon.automatetool.parser.ast.BlockStatement; +import com.annimon.automatetool.parser.ast.MoveStatement; +import com.annimon.automatetool.parser.ast.Statement; +import com.annimon.automatetool.parser.ast.ValueExpression; +import java.util.List; +import static com.annimon.automatetool.parser.ast.AstHelper.*; +import com.annimon.automatetool.parser.ast.BinaryExpression; +import com.annimon.automatetool.parser.ast.ClickStatement; +import com.annimon.automatetool.parser.ast.ConditionalExpression; +import com.annimon.automatetool.parser.ast.DelayStatement; +import com.annimon.automatetool.parser.ast.Expression; +import com.annimon.automatetool.parser.ast.ForStatement; +import com.annimon.automatetool.parser.ast.KeyPressStatement; +import com.annimon.automatetool.parser.ast.PressStatement; +import com.annimon.automatetool.parser.ast.ReleaseStatement; +import com.annimon.automatetool.parser.ast.TypeStatement; +import com.annimon.automatetool.parser.ast.VariableExpression; + +/** + * + * @author aNNiMON + */ +public final class Parser { + + private static final Token EOF = new Token(TokenType.EOF); + + private final int length; + private final List tokens; + private int pos; + + public Parser(List tokens) { + this.tokens = tokens; + length = tokens.size(); + pos = 0; + } + + public Statement parse() { + final BlockStatement program = new BlockStatement(); + while (!match(TokenType.EOF)) { + program.add(statement()); + } + /*program.add(block( + move(intValue(228), intValue(400)), + delay(intValue(400)), + move(intValue(322), intValue(400)), + delay(intValue(400)), + move(intValue(400), intValue(400)), + delay(intValue(400)), + move(intValue(415), intValue(400)), + delay(intValue(400)), + move(intValue(620), intValue(400)) + ));*/ + return program; + } + + private BlockStatement block() { + final BlockStatement block = new BlockStatement(); + consume(TokenType.LBRACE); + while (!match(TokenType.RBRACE)) { + block.add(statement()); + } + return block; + } + + private Statement blockOrStatement() { + if (lookMatch(0, TokenType.LBRACE)) { + return block(); + } + return statement(); + } + + private Statement statement() { + if (match(TokenType.MOVE)) { + return new MoveStatement(expression(), expression()); + } + if (match(TokenType.CLICK)) { + return new ClickStatement(expression()); + } + if (match(TokenType.DELAY)) { + return new DelayStatement(expression()); + } + if (match(TokenType.PRESS)) { + return new PressStatement(expression()); + } + if (match(TokenType.RELEASE)) { + return new ReleaseStatement(expression()); + } + if (match(TokenType.KEYPRESS)) { + return new KeyPressStatement(expression()); + } + if (match(TokenType.KEYRELEASE)) { + return new KeyPressStatement(expression()); + } + if (match(TokenType.TYPE)) { + return new TypeStatement(expression()); + } + + if (match(TokenType.FOR)) { + return forLoop(); + } + + if (lookMatch(0, TokenType.WORD) && lookMatch(1, TokenType.EQ)) { + final String variable = consume(TokenType.WORD).getText(); + consume(TokenType.EQ); + return new AssignmentStatement(variable, expression()); + } + + System.out.println(peek(0) + ", " + peek(1)); + throw new RuntimeException("Unknown statement: " + peek(0)); + } + + private Statement forLoop() { + match(TokenType.LPAREN); + final Statement initialization = statement(); + match(TokenType.COMMA); + final Expression termination = expression(); + match(TokenType.COMMA); + final Statement increment = statement(); + match(TokenType.RPAREN); + final Statement statement = blockOrStatement(); + return new ForStatement(initialization, termination, increment, statement); + } + + private Expression expression() { + return conditional(); + } + + private Expression conditional() { + Expression result = additive(); + + if (match(TokenType.LT)) { + return new ConditionalExpression( + ConditionalExpression.Operation.LESS, result, expression()); + } + if (match(TokenType.GT)) { + return new ConditionalExpression( + ConditionalExpression.Operation.GREATER, result, expression()); + } + return result; + } + + private Expression additive() { + Expression result = multiplicative(); + + while (true) { + if (match(TokenType.PLUS)) { + result = new BinaryExpression( + BinaryExpression.Operation.ADDITION, result, expression()); + continue; + } + if (match(TokenType.MINUS)) { + result = new BinaryExpression( + BinaryExpression.Operation.SUBSTRACTION, result, expression()); + continue; + } + break; + } + return result; + } + + private Expression multiplicative() { + Expression result = unary(); + + while (true) { + if (match(TokenType.STAR)) { + result = new BinaryExpression( + BinaryExpression.Operation.MULTIPLICATION, result, expression()); + continue; + } + if (match(TokenType.SLASH)) { + result = new BinaryExpression( + BinaryExpression.Operation.DIVISION, result, expression()); + continue; + } + break; + } + return result; + } + + private Expression unary() { + // TODO unary minus + return primary(); + } + + private Expression primary() { + final Token current = peek(0); + if (match(TokenType.NUMBER)) { + return new ValueExpression(Integer.parseInt(current.getText())); + } + if (match(TokenType.WORD)) { + return new VariableExpression(current.getText()); + } + if (match(TokenType.TEXT)) { + return new ValueExpression(current.getText()); + } + if (match(TokenType.LPAREN)) { + Expression result = expression(); + match(TokenType.RPAREN); + return result; + } + + throw new RuntimeException("Unknown expression: " + peek(0)); + } + + + private Token consume(TokenType type) { + final Token token = peek(0); + if (token.getType() != type) { + throw new RuntimeException("Wrong token. Expected: " + type); + } + pos++; + return token; + } + + private boolean match(TokenType type) { + final Token token = peek(0); + if (token.getType() != type) return false; + pos++; + return true; + } + + private boolean lookMatch(int relativePosition, TokenType type) { + return (peek(relativePosition).getType() == type); + } + + private Token peek(int relativePosition) { + final int position = pos + relativePosition; + if (position >= length) return EOF; + return tokens.get(position); + } +} diff --git a/src/com/annimon/automatetool/parser/Token.java b/src/com/annimon/automatetool/parser/Token.java new file mode 100644 index 0000000..d43c671 --- /dev/null +++ b/src/com/annimon/automatetool/parser/Token.java @@ -0,0 +1,33 @@ +package com.annimon.automatetool.parser; + +/** + * + * @author aNNiMON + */ +public final class Token { + + private final TokenType type; + private final String text; + + public Token(TokenType type) { + this(type, ""); + } + + public Token(TokenType type, String text) { + this.type = type; + this.text = text; + } + + public TokenType getType() { + return type; + } + + public String getText() { + return text; + } + + @Override + public String toString() { + return String.format("%s(%s)", type.name(), text); + } +} diff --git a/src/com/annimon/automatetool/parser/TokenType.java b/src/com/annimon/automatetool/parser/TokenType.java new file mode 100644 index 0000000..c41e440 --- /dev/null +++ b/src/com/annimon/automatetool/parser/TokenType.java @@ -0,0 +1,42 @@ +package com.annimon.automatetool.parser; + +/** + * + * @author aNNiMON + */ +public enum TokenType { + + NUMBER, + WORD, + TEXT, + + MOVE, + CLICK, + DELAY, + PRESS, + RELEASE, + KEYPRESS, + KEYRELEASE, + TYPE, + + FOR, + + + EQ, + LT, + GT, + + PLUS, + MINUS, + STAR, + SLASH, + + LPAREN, + RPAREN, + COMMA, + LBRACE, + RBRACE, + + EOL, + EOF, +} diff --git a/src/com/annimon/automatetool/parser/ast/AssignmentStatement.java b/src/com/annimon/automatetool/parser/ast/AssignmentStatement.java new file mode 100644 index 0000000..1a19eea --- /dev/null +++ b/src/com/annimon/automatetool/parser/ast/AssignmentStatement.java @@ -0,0 +1,28 @@ +package com.annimon.automatetool.parser.ast; + +import com.annimon.automatetool.lib.Variables; + +/** + * + * @author aNNiMON + */ +public final class AssignmentStatement implements Statement { + + private final String variable; + private final Expression expression; + + public AssignmentStatement(String variable, Expression expression) { + this.variable = variable; + this.expression = expression; + } + + @Override + public void execute() { + Variables.set(variable, expression.eval()); + } + + @Override + public String toString() { + return String.format("%s = %s", variable, expression); + } +} diff --git a/src/com/annimon/automatetool/parser/ast/AstHelper.java b/src/com/annimon/automatetool/parser/ast/AstHelper.java new file mode 100644 index 0000000..d8d49c8 --- /dev/null +++ b/src/com/annimon/automatetool/parser/ast/AstHelper.java @@ -0,0 +1,49 @@ +package com.annimon.automatetool.parser.ast; + +import com.annimon.automatetool.lib.IntegerValue; +import com.annimon.automatetool.lib.Value; + +/** + * + * @author aNNiMON + */ +public final class AstHelper { + + public static IntegerValue integer(int number) { + return new IntegerValue(number); + } + + public static ValueExpression value(Value value) { + return new ValueExpression(value); + } + + public static VariableExpression variable(String variable) { + return new VariableExpression(variable); + } + + public static ValueExpression intValue(int number) { + return value(integer(number)); + } + + + public static BlockStatement block(Statement... statements) { + final BlockStatement block = new BlockStatement(); + for (Statement statement : statements) { + block.add(statement); + } + return block; + } + + public static ForStatement forLoop(Statement initialization, Expression termination, + Statement increment, Statement block) { + return new ForStatement(initialization, termination, increment, block); + } + + public static MoveStatement move(Expression x, Expression y) { + return new MoveStatement(x, y); + } + + public static DelayStatement delay(Expression time) { + return new DelayStatement(time); + } +} diff --git a/src/com/annimon/automatetool/parser/ast/BinaryExpression.java b/src/com/annimon/automatetool/parser/ast/BinaryExpression.java new file mode 100644 index 0000000..37acb0d --- /dev/null +++ b/src/com/annimon/automatetool/parser/ast/BinaryExpression.java @@ -0,0 +1,60 @@ +package com.annimon.automatetool.parser.ast; + +import com.annimon.automatetool.lib.IntegerValue; +import com.annimon.automatetool.lib.Value; + +/** + * + * @author aNNiMON + */ +public final class BinaryExpression implements Expression { + + public enum Operation { + ADDITION("+"), + SUBSTRACTION("-"), + MULTIPLICATION("*"), + DIVISION("/"),; + + private final String operation; + + private Operation(String operation) { + this.operation = operation; + } + + public String getOperation() { + return operation; + } + } + + private final Operation operation; + private final Expression expr1, expr2; + + public BinaryExpression(Operation operator, Expression expr1, Expression expr2) { + this.operation = operator; + this.expr1 = expr1; + this.expr2 = expr2; + } + + @Override + public Value eval() { + final Value value1 = expr1.eval(); + final Value value2 = expr2.eval(); + // TODO check type (string) + switch (operation) { + case ADDITION: + return new IntegerValue(value1.asInteger() + value2.asInteger()); + case SUBSTRACTION: + return new IntegerValue(value1.asInteger() - value2.asInteger()); + case MULTIPLICATION: + return new IntegerValue(value1.asInteger() * value2.asInteger()); + case DIVISION: + return new IntegerValue(value1.asInteger() / value2.asInteger()); + } + throw new RuntimeException("Unknown operation: " + operation); + } + + @Override + public String toString() { + return String.format("%s %s %s", expr1, operation.getOperation(), expr2); + } +} diff --git a/src/com/annimon/automatetool/parser/ast/BlockStatement.java b/src/com/annimon/automatetool/parser/ast/BlockStatement.java new file mode 100644 index 0000000..32a6c41 --- /dev/null +++ b/src/com/annimon/automatetool/parser/ast/BlockStatement.java @@ -0,0 +1,40 @@ +package com.annimon.automatetool.parser.ast; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +/** + * + * @author aNNiMON + */ +public final class BlockStatement implements Statement { + + private final List statements; + + public BlockStatement() { + this.statements = new ArrayList<>(); + } + + public BlockStatement(List statements) { + this.statements = statements; + } + + public void add(Statement statement) { + statements.add(statement); + } + + @Override + public void execute() { + for (Statement statement : statements) { + statement.execute(); + } + } + + @Override + public String toString() { + return statements.stream() + .map(s -> s.toString()) + .collect(Collectors.joining("\n")); + } +} diff --git a/src/com/annimon/automatetool/parser/ast/ClickStatement.java b/src/com/annimon/automatetool/parser/ast/ClickStatement.java new file mode 100644 index 0000000..9b5cf63 --- /dev/null +++ b/src/com/annimon/automatetool/parser/ast/ClickStatement.java @@ -0,0 +1,26 @@ +package com.annimon.automatetool.parser.ast; + +import com.annimon.automatetool.RobotUtils; + +/** + * + * @author aNNiMON + */ +public final class ClickStatement implements Statement { + + private final Expression expression; + + public ClickStatement(Expression expression) { + this.expression = expression; + } + + @Override + public void execute() { + RobotUtils.click(expression.eval().asInteger()); + } + + @Override + public String toString() { + return String.format("click(%s)", expression); + } +} diff --git a/src/com/annimon/automatetool/parser/ast/ConditionalExpression.java b/src/com/annimon/automatetool/parser/ast/ConditionalExpression.java new file mode 100644 index 0000000..44575df --- /dev/null +++ b/src/com/annimon/automatetool/parser/ast/ConditionalExpression.java @@ -0,0 +1,54 @@ +package com.annimon.automatetool.parser.ast; + +import com.annimon.automatetool.lib.BooleanValue; +import com.annimon.automatetool.lib.Value; + +/** + * + * @author aNNiMON + */ +public final class ConditionalExpression implements Expression { + + public enum Operation { + LESS("<"), + GREATER(">"); + + private final String operation; + + private Operation(String operation) { + this.operation = operation; + } + + public String getOperation() { + return operation; + } + } + + private final Operation operation; + private final Expression expr1, expr2; + + public ConditionalExpression(Operation operator, Expression expr1, Expression expr2) { + this.operation = operator; + this.expr1 = expr1; + this.expr2 = expr2; + } + + @Override + public Value eval() { + final Value value1 = expr1.eval(); + final Value value2 = expr2.eval(); + // TODO check type (string) + switch (operation) { + case LESS: + return new BooleanValue(value1.asInteger() < value2.asInteger()); + case GREATER: + return new BooleanValue(value1.asInteger() > value2.asInteger()); + } + throw new RuntimeException("Unknown operation: " + operation); + } + + @Override + public String toString() { + return String.format("%s %s %s", expr1, operation.getOperation(), expr2); + } +} diff --git a/src/com/annimon/automatetool/parser/ast/DelayStatement.java b/src/com/annimon/automatetool/parser/ast/DelayStatement.java new file mode 100644 index 0000000..82c4b85 --- /dev/null +++ b/src/com/annimon/automatetool/parser/ast/DelayStatement.java @@ -0,0 +1,26 @@ +package com.annimon.automatetool.parser.ast; + +import com.annimon.automatetool.RobotUtils; + +/** + * + * @author aNNiMON + */ +public final class DelayStatement implements Statement { + + private final Expression expression; + + public DelayStatement(Expression expression) { + this.expression = expression; + } + + @Override + public void execute() { + RobotUtils.delay(expression.eval().asInteger()); + } + + @Override + public String toString() { + return String.format("delay(%s)", expression); + } +} diff --git a/src/com/annimon/automatetool/parser/ast/Expression.java b/src/com/annimon/automatetool/parser/ast/Expression.java new file mode 100644 index 0000000..02d91ac --- /dev/null +++ b/src/com/annimon/automatetool/parser/ast/Expression.java @@ -0,0 +1,12 @@ +package com.annimon.automatetool.parser.ast; + +import com.annimon.automatetool.lib.Value; + +/** + * + * @author aNNiMON + */ +public interface Expression extends Node { + + Value eval(); +} diff --git a/src/com/annimon/automatetool/parser/ast/ForStatement.java b/src/com/annimon/automatetool/parser/ast/ForStatement.java new file mode 100644 index 0000000..8501495 --- /dev/null +++ b/src/com/annimon/automatetool/parser/ast/ForStatement.java @@ -0,0 +1,31 @@ +package com.annimon.automatetool.parser.ast; + +/** + * + * @author aNNiMON + */ +public final class ForStatement implements Statement { + + private final Statement initialization, increment; + private final Expression termination; + private final Statement block; + + public ForStatement(Statement initialization, Expression termination, Statement increment, Statement block) { + this.initialization = initialization; + this.termination = termination; + this.increment = increment; + this.block = block; + } + + @Override + public void execute() { + for (initialization.execute(); termination.eval().asBoolean(); increment.execute()) { + block.execute(); + } + } + + @Override + public String toString() { + return String.format("for (%s; %s; %s) %s", initialization, termination, increment, block); + } +} diff --git a/src/com/annimon/automatetool/parser/ast/KeyPressStatement.java b/src/com/annimon/automatetool/parser/ast/KeyPressStatement.java new file mode 100644 index 0000000..c9ad5ef --- /dev/null +++ b/src/com/annimon/automatetool/parser/ast/KeyPressStatement.java @@ -0,0 +1,26 @@ +package com.annimon.automatetool.parser.ast; + +import com.annimon.automatetool.RobotUtils; + +/** + * + * @author aNNiMON + */ +public final class KeyPressStatement implements Statement { + + private final Expression key; + + public KeyPressStatement(Expression key) { + this.key = key; + } + + @Override + public void execute() { + RobotUtils.keyPress(key.eval().asInteger()); + } + + @Override + public String toString() { + return String.format("keypress(%s)", key); + } +} diff --git a/src/com/annimon/automatetool/parser/ast/KeyReleaseStatement.java b/src/com/annimon/automatetool/parser/ast/KeyReleaseStatement.java new file mode 100644 index 0000000..2c09c0a --- /dev/null +++ b/src/com/annimon/automatetool/parser/ast/KeyReleaseStatement.java @@ -0,0 +1,26 @@ +package com.annimon.automatetool.parser.ast; + +import com.annimon.automatetool.RobotUtils; + +/** + * + * @author aNNiMON + */ +public final class KeyReleaseStatement implements Statement { + + private final Expression key; + + public KeyReleaseStatement(Expression key) { + this.key = key; + } + + @Override + public void execute() { + RobotUtils.keyRelease(key.eval().asInteger()); + } + + @Override + public String toString() { + return String.format("keyrelease(%s)", key); + } +} diff --git a/src/com/annimon/automatetool/parser/ast/MoveStatement.java b/src/com/annimon/automatetool/parser/ast/MoveStatement.java new file mode 100644 index 0000000..22728bd --- /dev/null +++ b/src/com/annimon/automatetool/parser/ast/MoveStatement.java @@ -0,0 +1,30 @@ +package com.annimon.automatetool.parser.ast; + +import com.annimon.automatetool.RobotUtils; +import com.annimon.automatetool.lib.Value; + +/** + * + * @author aNNiMON + */ +public final class MoveStatement implements Statement { + + private final Expression xExpr, yExpr; + + public MoveStatement(Expression x, Expression y) { + this.xExpr = x; + this.yExpr = y; + } + + @Override + public void execute() { + final Value xValue = xExpr.eval(); + final Value yValue = yExpr.eval(); + RobotUtils.mouseMove(xValue.asInteger(), yValue.asInteger()); + } + + @Override + public String toString() { + return String.format("move(%s, %s)", xExpr, yExpr); + } +} diff --git a/src/com/annimon/automatetool/parser/ast/Node.java b/src/com/annimon/automatetool/parser/ast/Node.java new file mode 100644 index 0000000..f88556b --- /dev/null +++ b/src/com/annimon/automatetool/parser/ast/Node.java @@ -0,0 +1,9 @@ +package com.annimon.automatetool.parser.ast; + +/** + * + * @author aNNiMON + */ +public interface Node { + +} diff --git a/src/com/annimon/automatetool/parser/ast/PressStatement.java b/src/com/annimon/automatetool/parser/ast/PressStatement.java new file mode 100644 index 0000000..3461517 --- /dev/null +++ b/src/com/annimon/automatetool/parser/ast/PressStatement.java @@ -0,0 +1,26 @@ +package com.annimon.automatetool.parser.ast; + +import com.annimon.automatetool.RobotUtils; + +/** + * + * @author aNNiMON + */ +public final class PressStatement implements Statement { + + private final Expression button; + + public PressStatement(Expression button) { + this.button = button; + } + + @Override + public void execute() { + RobotUtils.mousePress(button.eval().asInteger()); + } + + @Override + public String toString() { + return String.format("press(%s)", button); + } +} diff --git a/src/com/annimon/automatetool/parser/ast/ReleaseStatement.java b/src/com/annimon/automatetool/parser/ast/ReleaseStatement.java new file mode 100644 index 0000000..2483efc --- /dev/null +++ b/src/com/annimon/automatetool/parser/ast/ReleaseStatement.java @@ -0,0 +1,26 @@ +package com.annimon.automatetool.parser.ast; + +import com.annimon.automatetool.RobotUtils; + +/** + * + * @author aNNiMON + */ +public final class ReleaseStatement implements Statement { + + private final Expression button; + + public ReleaseStatement(Expression button) { + this.button = button; + } + + @Override + public void execute() { + RobotUtils.mouseRelease(button.eval().asInteger()); + } + + @Override + public String toString() { + return String.format("release(%s)", button); + } +} diff --git a/src/com/annimon/automatetool/parser/ast/Statement.java b/src/com/annimon/automatetool/parser/ast/Statement.java new file mode 100644 index 0000000..145be29 --- /dev/null +++ b/src/com/annimon/automatetool/parser/ast/Statement.java @@ -0,0 +1,10 @@ +package com.annimon.automatetool.parser.ast; + +/** + * + * @author aNNiMON + */ +public interface Statement { + + void execute(); +} diff --git a/src/com/annimon/automatetool/parser/ast/TypeStatement.java b/src/com/annimon/automatetool/parser/ast/TypeStatement.java new file mode 100644 index 0000000..8188e88 --- /dev/null +++ b/src/com/annimon/automatetool/parser/ast/TypeStatement.java @@ -0,0 +1,26 @@ +package com.annimon.automatetool.parser.ast; + +import com.annimon.automatetool.RobotUtils; + +/** + * + * @author aNNiMON + */ +public final class TypeStatement implements Statement { + + private final Expression message; + + public TypeStatement(Expression message) { + this.message = message; + } + + @Override + public void execute() { + RobotUtils.typeText(message.eval().asString()); + } + + @Override + public String toString() { + return String.format("type(%s)", message); + } +} diff --git a/src/com/annimon/automatetool/parser/ast/ValueExpression.java b/src/com/annimon/automatetool/parser/ast/ValueExpression.java new file mode 100644 index 0000000..d8ee8ff --- /dev/null +++ b/src/com/annimon/automatetool/parser/ast/ValueExpression.java @@ -0,0 +1,41 @@ +package com.annimon.automatetool.parser.ast; + +import com.annimon.automatetool.lib.BooleanValue; +import com.annimon.automatetool.lib.IntegerValue; +import com.annimon.automatetool.lib.StringValue; +import com.annimon.automatetool.lib.Value; + +/** + * + * @author aNNiMON + */ +public final class ValueExpression implements Expression { + + private final Value value; + + public ValueExpression(boolean value) { + this.value = new BooleanValue(value); + } + + public ValueExpression(int value) { + this.value = new IntegerValue(value); + } + + public ValueExpression(String value) { + this.value = new StringValue(value); + } + + public ValueExpression(Value value) { + this.value = value; + } + + @Override + public Value eval() { + return value; + } + + @Override + public String toString() { + return value.toString(); + } +} diff --git a/src/com/annimon/automatetool/parser/ast/VariableExpression.java b/src/com/annimon/automatetool/parser/ast/VariableExpression.java new file mode 100644 index 0000000..2dc63e9 --- /dev/null +++ b/src/com/annimon/automatetool/parser/ast/VariableExpression.java @@ -0,0 +1,27 @@ +package com.annimon.automatetool.parser.ast; + +import com.annimon.automatetool.lib.Value; +import com.annimon.automatetool.lib.Variables; + +/** + * + * @author aNNiMON + */ +public final class VariableExpression implements Expression { + + private final String variable; + + public VariableExpression(String variable) { + this.variable = variable; + } + + @Override + public Value eval() { + return Variables.get(variable); + } + + @Override + public String toString() { + return variable; + } +}