Initial
This commit is contained in:
commit
9144e65f7e
60
Main.j
Normal file
60
Main.j
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
; Main.j
|
||||||
|
; Generated by ClassFileAnalyzer (Can)
|
||||||
|
|
||||||
|
.bytecode 49.0
|
||||||
|
.class public Main
|
||||||
|
; Flag ACC_SUPER not set, see JVM spec
|
||||||
|
.super java/lang/Object
|
||||||
|
|
||||||
|
.field private static x D
|
||||||
|
.field private static y I
|
||||||
|
|
||||||
|
.method public <init>()V
|
||||||
|
.limit stack 1
|
||||||
|
.limit locals 1
|
||||||
|
0: aload_0
|
||||||
|
1: invokespecial java/lang/Object/<init>()V
|
||||||
|
4: return
|
||||||
|
.end method
|
||||||
|
|
||||||
|
.method public static main([Ljava/lang/String;)V
|
||||||
|
.limit stack 6
|
||||||
|
.limit locals 1
|
||||||
|
0: ldc 15
|
||||||
|
2: i2d
|
||||||
|
3: putstatic Main/x D
|
||||||
|
6: ldc 20
|
||||||
|
8: putstatic Main/y I
|
||||||
|
11: getstatic java/lang/System/out Ljava/io/PrintStream;
|
||||||
|
14: new java/lang/StringBuilder
|
||||||
|
17: dup
|
||||||
|
18: invokespecial java/lang/StringBuilder/<init>()V
|
||||||
|
21: ldc "x + y = "
|
||||||
|
23: invokevirtual java/lang/StringBuilder/append(Ljava/lang/String;)Ljava/lang/StringBuilder;
|
||||||
|
26: getstatic Main/x D
|
||||||
|
29: getstatic Main/y I
|
||||||
|
32: i2d
|
||||||
|
33: dadd
|
||||||
|
34: invokevirtual java/lang/StringBuilder/append(D)Ljava/lang/StringBuilder;
|
||||||
|
37: invokevirtual java/lang/StringBuilder/toString()Ljava/lang/String;
|
||||||
|
40: invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V
|
||||||
|
43: getstatic java/lang/System/out Ljava/io/PrintStream;
|
||||||
|
46: ldc 20
|
||||||
|
48: ldc 5
|
||||||
|
50: idiv
|
||||||
|
51: ldc 2
|
||||||
|
53: ldc 2
|
||||||
|
55: iadd
|
||||||
|
56: ldc 2
|
||||||
|
58: imul
|
||||||
|
59: iadd
|
||||||
|
60: ldc 2
|
||||||
|
62: ldc 2
|
||||||
|
64: ldc 2
|
||||||
|
66: imul
|
||||||
|
67: iadd
|
||||||
|
68: isub
|
||||||
|
69: invokevirtual java/io/PrintStream/println(I)V
|
||||||
|
72: return
|
||||||
|
.end method
|
||||||
|
|
73
build.xml
Normal file
73
build.xml
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!-- You may freely edit this file. See commented blocks below for -->
|
||||||
|
<!-- some examples of how to customize the build. -->
|
||||||
|
<!-- (If you delete it and reopen the project it will be recreated.) -->
|
||||||
|
<!-- By default, only the Clean and Build commands use this build script. -->
|
||||||
|
<!-- Commands such as Run, Debug, and Test only use this build script if -->
|
||||||
|
<!-- the Compile on Save feature is turned off for the project. -->
|
||||||
|
<!-- You can turn off the Compile on Save (or Deploy on Save) setting -->
|
||||||
|
<!-- in the project's Project Properties dialog box.-->
|
||||||
|
<project name="StyLang" default="default" basedir=".">
|
||||||
|
<description>Builds, tests, and runs the project StyLang.</description>
|
||||||
|
<import file="nbproject/build-impl.xml"/>
|
||||||
|
<!--
|
||||||
|
|
||||||
|
There exist several targets which are by default empty and which can be
|
||||||
|
used for execution of your tasks. These targets are usually executed
|
||||||
|
before and after some main targets. They are:
|
||||||
|
|
||||||
|
-pre-init: called before initialization of project properties
|
||||||
|
-post-init: called after initialization of project properties
|
||||||
|
-pre-compile: called before javac compilation
|
||||||
|
-post-compile: called after javac compilation
|
||||||
|
-pre-compile-single: called before javac compilation of single file
|
||||||
|
-post-compile-single: called after javac compilation of single file
|
||||||
|
-pre-compile-test: called before javac compilation of JUnit tests
|
||||||
|
-post-compile-test: called after javac compilation of JUnit tests
|
||||||
|
-pre-compile-test-single: called before javac compilation of single JUnit test
|
||||||
|
-post-compile-test-single: called after javac compilation of single JUunit test
|
||||||
|
-pre-jar: called before JAR building
|
||||||
|
-post-jar: called after JAR building
|
||||||
|
-post-clean: called after cleaning build products
|
||||||
|
|
||||||
|
(Targets beginning with '-' are not intended to be called on their own.)
|
||||||
|
|
||||||
|
Example of inserting an obfuscator after compilation could look like this:
|
||||||
|
|
||||||
|
<target name="-post-compile">
|
||||||
|
<obfuscate>
|
||||||
|
<fileset dir="${build.classes.dir}"/>
|
||||||
|
</obfuscate>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
For list of available properties check the imported
|
||||||
|
nbproject/build-impl.xml file.
|
||||||
|
|
||||||
|
|
||||||
|
Another way to customize the build is by overriding existing main targets.
|
||||||
|
The targets of interest are:
|
||||||
|
|
||||||
|
-init-macrodef-javac: defines macro for javac compilation
|
||||||
|
-init-macrodef-junit: defines macro for junit execution
|
||||||
|
-init-macrodef-debug: defines macro for class debugging
|
||||||
|
-init-macrodef-java: defines macro for class execution
|
||||||
|
-do-jar: JAR building
|
||||||
|
run: execution of project
|
||||||
|
-javadoc-build: Javadoc generation
|
||||||
|
test-report: JUnit report generation
|
||||||
|
|
||||||
|
An example of overriding the target for project execution could look like this:
|
||||||
|
|
||||||
|
<target name="run" depends="StyLang-impl.jar">
|
||||||
|
<exec dir="bin" executable="launcher.exe">
|
||||||
|
<arg file="${dist.jar}"/>
|
||||||
|
</exec>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
Notice that the overridden target depends on the jar target and not only on
|
||||||
|
the compile target as the regular run target does. Again, for a list of available
|
||||||
|
properties which you can use, check the target you are overriding in the
|
||||||
|
nbproject/build-impl.xml file.
|
||||||
|
|
||||||
|
-->
|
||||||
|
</project>
|
6
input.txt
Normal file
6
input.txt
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
double x = (double)15;
|
||||||
|
int y = 20;
|
||||||
|
// print "x + y = " + (x + y);
|
||||||
|
print "x + y = " + (x + (double)y);
|
||||||
|
|
||||||
|
print 20 / 5 + (2 + 2) * 2 - (2 + 2 * 2);
|
1419
nbproject/build-impl.xml
Normal file
1419
nbproject/build-impl.xml
Normal file
File diff suppressed because it is too large
Load Diff
8
nbproject/genfiles.properties
Normal file
8
nbproject/genfiles.properties
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
build.xml.data.CRC32=59061f6b
|
||||||
|
build.xml.script.CRC32=1fd52470
|
||||||
|
build.xml.stylesheet.CRC32=8064a381@1.78.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=59061f6b
|
||||||
|
nbproject/build-impl.xml.script.CRC32=45404d2a
|
||||||
|
nbproject/build-impl.xml.stylesheet.CRC32=9ca7cb75@1.80.0.48
|
0
nbproject/private/config.properties
Normal file
0
nbproject/private/config.properties
Normal file
6
nbproject/private/private.properties
Normal file
6
nbproject/private/private.properties
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
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
|
7
nbproject/private/private.xml
Normal file
7
nbproject/private/private.xml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project-private xmlns="http://www.netbeans.org/ns/project-private/1">
|
||||||
|
<editor-bookmarks xmlns="http://www.netbeans.org/ns/editor-bookmarks/2" lastBookmarkId="0"/>
|
||||||
|
<open-files xmlns="http://www.netbeans.org/ns/projectui-open-files/2">
|
||||||
|
<group name="_TUTORIALS"/>
|
||||||
|
</open-files>
|
||||||
|
</project-private>
|
80
nbproject/project.properties
Normal file
80
nbproject/project.properties
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
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=StyLang
|
||||||
|
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}/StyLang.jar
|
||||||
|
dist.javadoc.dir=${dist.dir}/javadoc
|
||||||
|
endorsed.classpath=
|
||||||
|
excludes=
|
||||||
|
file.reference.asm-5.0.4.jar=asm-5.0.4.jar
|
||||||
|
includes=**
|
||||||
|
jar.compress=false
|
||||||
|
javac.classpath=\
|
||||||
|
${file.reference.asm-5.0.4.jar}
|
||||||
|
# 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.reference.asm-5.0.4.jar=D:\\dev\\__frameworks\\asm-5.0.4\\doc\\javadoc\\user
|
||||||
|
javadoc.splitindex=true
|
||||||
|
javadoc.use=true
|
||||||
|
javadoc.version=false
|
||||||
|
javadoc.windowtitle=
|
||||||
|
main.class=com.annimon.stylang.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
|
||||||
|
source.reference.asm-5.0.4.jar=D:\\dev\\__frameworks\\asm-5.0.4\\src.zip
|
||||||
|
src.dir=src
|
||||||
|
test.src.dir=test
|
15
nbproject/project.xml
Normal file
15
nbproject/project.xml
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://www.netbeans.org/ns/project/1">
|
||||||
|
<type>org.netbeans.modules.java.j2seproject</type>
|
||||||
|
<configuration>
|
||||||
|
<data xmlns="http://www.netbeans.org/ns/j2se-project/3">
|
||||||
|
<name>StyLang</name>
|
||||||
|
<source-roots>
|
||||||
|
<root id="src.dir"/>
|
||||||
|
</source-roots>
|
||||||
|
<test-roots>
|
||||||
|
<root id="test.src.dir"/>
|
||||||
|
</test-roots>
|
||||||
|
</data>
|
||||||
|
</configuration>
|
||||||
|
</project>
|
46
src/com/annimon/stylang/Main.java
Normal file
46
src/com/annimon/stylang/Main.java
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
package com.annimon.stylang;
|
||||||
|
|
||||||
|
import com.annimon.stylang.parser.*;
|
||||||
|
import com.annimon.stylang.parser.ast.Statement;
|
||||||
|
import com.annimon.stylang.parser.visitors.ClassCompiler;
|
||||||
|
import com.annimon.stylang.parser.visitors.ConstantPoolPrinter;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author aNNiMON
|
||||||
|
*/
|
||||||
|
public final class Main {
|
||||||
|
|
||||||
|
public static void main(String[] args) throws IOException {
|
||||||
|
final String input = new String( Files.readAllBytes(Paths.get("input.txt")), "UTF-8");
|
||||||
|
final List<Token> tokens = new Lexer(input).tokenize();
|
||||||
|
for (Token token : tokens) {
|
||||||
|
System.out.println(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
final Statement program = new Parser(tokens).parse();
|
||||||
|
System.out.println(program.toString());
|
||||||
|
program.execute();
|
||||||
|
|
||||||
|
System.out.println("\nConstant Pool");
|
||||||
|
program.accept(new ConstantPoolPrinter());
|
||||||
|
|
||||||
|
// Компиляция
|
||||||
|
final ClassCompiler compiler = new ClassCompiler();
|
||||||
|
final byte[] classRaw = compiler.compile(program);
|
||||||
|
Files.write(Paths.get("Main.class"), classRaw);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ma() {
|
||||||
|
double x = (double)15;
|
||||||
|
int y = 20;
|
||||||
|
System.out.println("x + y = " + (x + y));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String concat(Object s1, Object s2) {
|
||||||
|
return new StringBuilder().append(s1).append(s2).toString();
|
||||||
|
}
|
||||||
|
}
|
39
src/com/annimon/stylang/lib/DoubleValue.java
Normal file
39
src/com/annimon/stylang/lib/DoubleValue.java
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
package com.annimon.stylang.lib;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author aNNiMON
|
||||||
|
*/
|
||||||
|
public final class DoubleValue implements Value {
|
||||||
|
|
||||||
|
public static final DoubleValue ZERO = new DoubleValue(0);
|
||||||
|
|
||||||
|
public static double get(Value value) {
|
||||||
|
return ((DoubleValue) value).value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private final double value;
|
||||||
|
|
||||||
|
public DoubleValue(double value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Type type() {
|
||||||
|
return Type.DOUBLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object raw() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return Double.toString(value);
|
||||||
|
}
|
||||||
|
}
|
39
src/com/annimon/stylang/lib/IntValue.java
Normal file
39
src/com/annimon/stylang/lib/IntValue.java
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
package com.annimon.stylang.lib;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author aNNiMON
|
||||||
|
*/
|
||||||
|
public final class IntValue implements Value {
|
||||||
|
|
||||||
|
public static int get(Value value) {
|
||||||
|
return ((IntValue) value).value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final IntValue ZERO = new IntValue(0);
|
||||||
|
|
||||||
|
private final int value;
|
||||||
|
|
||||||
|
public IntValue(int value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Type type() {
|
||||||
|
return Type.INT;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object raw() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return Integer.toString(value);
|
||||||
|
}
|
||||||
|
}
|
37
src/com/annimon/stylang/lib/StringValue.java
Normal file
37
src/com/annimon/stylang/lib/StringValue.java
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
package com.annimon.stylang.lib;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author aNNiMON
|
||||||
|
*/
|
||||||
|
public final class StringValue implements Value {
|
||||||
|
|
||||||
|
public static String get(Value value) {
|
||||||
|
return ((StringValue) value).value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private final String value;
|
||||||
|
|
||||||
|
public StringValue(String value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Type type() {
|
||||||
|
return Type.STRING;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object raw() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
46
src/com/annimon/stylang/lib/Type.java
Normal file
46
src/com/annimon/stylang/lib/Type.java
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
package com.annimon.stylang.lib;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author aNNiMON
|
||||||
|
*/
|
||||||
|
public interface Type {
|
||||||
|
|
||||||
|
public static final Type INVALID = new Type() {
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "INVALID";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public static final Type INT = new Type() {
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "int";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
public static final Type LONG = new Type() {
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "long";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
public static final Type FLOAT = new Type() {
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "float";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
public static final Type DOUBLE = new Type() {
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "double";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
public static final Type STRING = new Type() {
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "string";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
12
src/com/annimon/stylang/lib/Value.java
Normal file
12
src/com/annimon/stylang/lib/Value.java
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
package com.annimon.stylang.lib;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author aNNiMON
|
||||||
|
*/
|
||||||
|
public interface Value {
|
||||||
|
|
||||||
|
Type type();
|
||||||
|
|
||||||
|
Object raw();
|
||||||
|
}
|
51
src/com/annimon/stylang/lib/Variables.java
Normal file
51
src/com/annimon/stylang/lib/Variables.java
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
package com.annimon.stylang.lib;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Stack;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author aNNiMON
|
||||||
|
*/
|
||||||
|
public final class Variables {
|
||||||
|
|
||||||
|
private static final Stack<Map<String, Value>> stack;
|
||||||
|
private static Map<String, Value> variables;
|
||||||
|
private static Map<String, Type> types;
|
||||||
|
|
||||||
|
static {
|
||||||
|
stack = new Stack<>();
|
||||||
|
variables = new HashMap<>();
|
||||||
|
types = new HashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void push() {
|
||||||
|
stack.push(new HashMap<>(variables));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void pop() {
|
||||||
|
variables = stack.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isExists(String key) {
|
||||||
|
return variables.containsKey(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Value get(String key) {
|
||||||
|
if (!isExists(key)) throw new RuntimeException("Variable " + key + " not found");
|
||||||
|
return variables.get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void set(String key, Value value) {
|
||||||
|
variables.put(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Type getType(String key) {
|
||||||
|
return types.get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setType(String key, Type type) {
|
||||||
|
types.put(key, type);
|
||||||
|
}
|
||||||
|
}
|
232
src/com/annimon/stylang/parser/Lexer.java
Normal file
232
src/com/annimon/stylang/parser/Lexer.java
Normal file
@ -0,0 +1,232 @@
|
|||||||
|
package com.annimon.stylang.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 String OPERATOR_CHARS = "+-*/(){}=<>!&|,;";
|
||||||
|
|
||||||
|
private static final Map<String, TokenType> OPERATORS;
|
||||||
|
static {
|
||||||
|
OPERATORS = new HashMap<>();
|
||||||
|
OPERATORS.put("+", TokenType.PLUS);
|
||||||
|
OPERATORS.put("-", TokenType.MINUS);
|
||||||
|
OPERATORS.put("*", TokenType.STAR);
|
||||||
|
OPERATORS.put("/", TokenType.SLASH);
|
||||||
|
OPERATORS.put("(", TokenType.LPAREN);
|
||||||
|
OPERATORS.put(")", TokenType.RPAREN);
|
||||||
|
OPERATORS.put("{", TokenType.LBRACE);
|
||||||
|
OPERATORS.put("}", TokenType.RBRACE);
|
||||||
|
OPERATORS.put("=", TokenType.EQ);
|
||||||
|
OPERATORS.put("<", TokenType.LT);
|
||||||
|
OPERATORS.put(">", TokenType.GT);
|
||||||
|
OPERATORS.put(",", TokenType.COMMA);
|
||||||
|
OPERATORS.put(";", TokenType.SEMICOLON);
|
||||||
|
|
||||||
|
OPERATORS.put("!", TokenType.EXCL);
|
||||||
|
OPERATORS.put("&", TokenType.AMP);
|
||||||
|
OPERATORS.put("|", TokenType.BAR);
|
||||||
|
|
||||||
|
OPERATORS.put("==", TokenType.EQEQ);
|
||||||
|
OPERATORS.put("!=", TokenType.EXCLEQ);
|
||||||
|
OPERATORS.put("<=", TokenType.LTEQ);
|
||||||
|
OPERATORS.put(">=", TokenType.GTEQ);
|
||||||
|
|
||||||
|
OPERATORS.put("&&", TokenType.AMPAMP);
|
||||||
|
OPERATORS.put("||", TokenType.BARBAR);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final String input;
|
||||||
|
private final int length;
|
||||||
|
|
||||||
|
private final List<Token> tokens;
|
||||||
|
|
||||||
|
private int pos;
|
||||||
|
|
||||||
|
public Lexer(String input) {
|
||||||
|
this.input = input;
|
||||||
|
length = input.length();
|
||||||
|
|
||||||
|
tokens = new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Token> 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 == '#') {
|
||||||
|
next();
|
||||||
|
tokenizeHexNumber();
|
||||||
|
}
|
||||||
|
else if (OPERATOR_CHARS.indexOf(current) != -1) {
|
||||||
|
tokenizeOperator();
|
||||||
|
} else {
|
||||||
|
// whitespaces
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tokens;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void tokenizeNumber() {
|
||||||
|
final StringBuilder buffer = new StringBuilder();
|
||||||
|
char current = peek(0);
|
||||||
|
while (true) {
|
||||||
|
if (current == '.') {
|
||||||
|
if (buffer.indexOf(".") != -1) throw new RuntimeException("Invalid float number");
|
||||||
|
} else if (!Character.isDigit(current)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
buffer.append(current);
|
||||||
|
current = next();
|
||||||
|
}
|
||||||
|
addToken(TokenType.NUMBER, buffer.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void tokenizeHexNumber() {
|
||||||
|
final StringBuilder buffer = new StringBuilder();
|
||||||
|
char current = peek(0);
|
||||||
|
while (Character.isDigit(current) || isHexNumber(current)) {
|
||||||
|
buffer.append(current);
|
||||||
|
current = next();
|
||||||
|
}
|
||||||
|
addToken(TokenType.HEX_NUMBER, buffer.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isHexNumber(char current) {
|
||||||
|
return "abcdef".indexOf(Character.toLowerCase(current)) != -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void tokenizeOperator() {
|
||||||
|
char current = peek(0);
|
||||||
|
if (current == '/') {
|
||||||
|
if (peek(1) == '/') {
|
||||||
|
next();
|
||||||
|
next();
|
||||||
|
tokenizeComment();
|
||||||
|
return;
|
||||||
|
} else if (peek(1) == '*') {
|
||||||
|
next();
|
||||||
|
next();
|
||||||
|
tokenizeMultilineComment();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
final StringBuilder buffer = new StringBuilder();
|
||||||
|
while (true) {
|
||||||
|
final String text = buffer.toString();
|
||||||
|
if (!OPERATORS.containsKey(text + current) && !text.isEmpty()) {
|
||||||
|
addToken(OPERATORS.get(text));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
buffer.append(current);
|
||||||
|
current = next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void tokenizeWord() {
|
||||||
|
final StringBuilder buffer = new StringBuilder();
|
||||||
|
char current = peek(0);
|
||||||
|
while (true) {
|
||||||
|
if (!Character.isLetterOrDigit(current) && (current != '_') && (current != '$')) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
buffer.append(current);
|
||||||
|
current = next();
|
||||||
|
}
|
||||||
|
|
||||||
|
final String word = buffer.toString();
|
||||||
|
switch (word) {
|
||||||
|
case "int": addToken(TokenType.INT); break;
|
||||||
|
case "long": addToken(TokenType.LONG); break;
|
||||||
|
case "float": addToken(TokenType.FLOAT); break;
|
||||||
|
case "double": addToken(TokenType.DOUBLE); break;
|
||||||
|
case "string": addToken(TokenType.STRING); break;
|
||||||
|
case "var": addToken(TokenType.VAR); break;
|
||||||
|
|
||||||
|
case "print": addToken(TokenType.PRINT); break;
|
||||||
|
case "if": addToken(TokenType.IF); break;
|
||||||
|
case "else": addToken(TokenType.ELSE); break;
|
||||||
|
case "while": addToken(TokenType.WHILE); break;
|
||||||
|
case "for": addToken(TokenType.FOR); break;
|
||||||
|
case "do": addToken(TokenType.DO); break;
|
||||||
|
case "break": addToken(TokenType.BREAK); break;
|
||||||
|
case "continue": addToken(TokenType.CONTINUE); break;
|
||||||
|
case "def": addToken(TokenType.DEF); break;
|
||||||
|
case "return": addToken(TokenType.RETURN); break;
|
||||||
|
default:
|
||||||
|
addToken(TokenType.WORD, word);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void tokenizeText() {
|
||||||
|
next();// skip "
|
||||||
|
final StringBuilder buffer = new StringBuilder();
|
||||||
|
char current = peek(0);
|
||||||
|
while (true) {
|
||||||
|
if (current == '\\') {
|
||||||
|
current = next();
|
||||||
|
switch (current) {
|
||||||
|
case '"': current = next(); buffer.append('"'); continue;
|
||||||
|
case 'n': current = next(); buffer.append('\n'); continue;
|
||||||
|
case 't': current = next(); buffer.append('\t'); continue;
|
||||||
|
}
|
||||||
|
buffer.append('\\');
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (current == '"') break;
|
||||||
|
buffer.append(current);
|
||||||
|
current = next();
|
||||||
|
}
|
||||||
|
next(); // skip closing "
|
||||||
|
|
||||||
|
addToken(TokenType.TEXT, buffer.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void tokenizeComment() {
|
||||||
|
char current = peek(0);
|
||||||
|
while ("\r\n\0".indexOf(current) == -1) {
|
||||||
|
current = next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void tokenizeMultilineComment() {
|
||||||
|
char current = peek(0);
|
||||||
|
while (true) {
|
||||||
|
if (current == '\0') throw new RuntimeException("Missing close tag");
|
||||||
|
if (current == '*' && peek(1) == '/') break;
|
||||||
|
current = next();
|
||||||
|
}
|
||||||
|
next(); // *
|
||||||
|
next(); // /
|
||||||
|
}
|
||||||
|
|
||||||
|
private char next() {
|
||||||
|
pos++;
|
||||||
|
return peek(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private char peek(int relativePosition) {
|
||||||
|
final int position = pos + relativePosition;
|
||||||
|
if (position >= length) return '\0';
|
||||||
|
return input.charAt(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addToken(TokenType type) {
|
||||||
|
addToken(type, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addToken(TokenType type, String text) {
|
||||||
|
tokens.add(new Token(type, text));
|
||||||
|
}
|
||||||
|
}
|
197
src/com/annimon/stylang/parser/Parser.java
Normal file
197
src/com/annimon/stylang/parser/Parser.java
Normal file
@ -0,0 +1,197 @@
|
|||||||
|
package com.annimon.stylang.parser;
|
||||||
|
|
||||||
|
import com.annimon.stylang.lib.DoubleValue;
|
||||||
|
import com.annimon.stylang.lib.IntValue;
|
||||||
|
import com.annimon.stylang.lib.Type;
|
||||||
|
import com.annimon.stylang.lib.Value;
|
||||||
|
import com.annimon.stylang.parser.ast.*;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author aNNiMON
|
||||||
|
*/
|
||||||
|
public final class Parser {
|
||||||
|
|
||||||
|
private static final Token EOF = new Token(TokenType.EOF, "");
|
||||||
|
|
||||||
|
private final List<Token> tokens;
|
||||||
|
private final int size;
|
||||||
|
|
||||||
|
private int pos;
|
||||||
|
|
||||||
|
public Parser(List<Token> tokens) {
|
||||||
|
this.tokens = tokens;
|
||||||
|
size = tokens.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Statement parse() {
|
||||||
|
final BlockStatement result = new BlockStatement();
|
||||||
|
while (!match(TokenType.EOF)) {
|
||||||
|
result.add(statement());
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Statement block() {
|
||||||
|
final BlockStatement block = new BlockStatement();
|
||||||
|
consume(TokenType.LBRACE);
|
||||||
|
while (!match(TokenType.RBRACE)) {
|
||||||
|
block.add(statement());
|
||||||
|
}
|
||||||
|
return block;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Statement statementOrBlock() {
|
||||||
|
if (lookMatch(0, TokenType.LBRACE)) return block();
|
||||||
|
return statement();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Statement statement() {
|
||||||
|
if (match(TokenType.PRINT)) {
|
||||||
|
final Expression expression = expression();
|
||||||
|
consume(TokenType.SEMICOLON);
|
||||||
|
return new PrintStatement(expression);
|
||||||
|
}
|
||||||
|
return assignmentStatement();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Statement assignmentStatement() {
|
||||||
|
// WORD EQ
|
||||||
|
final Type type = type();
|
||||||
|
final Token current = get(0);
|
||||||
|
if (match(TokenType.WORD) && lookMatch(0, TokenType.EQ)) {
|
||||||
|
final String variable = current.getText();
|
||||||
|
consume(TokenType.EQ);
|
||||||
|
final Expression expression = expression();
|
||||||
|
final Type exprType = expression.type();
|
||||||
|
consume(TokenType.SEMICOLON);
|
||||||
|
if (exprType != type) throw new RuntimeException("Type mismatch. Expect " + type + ", but found " + exprType);
|
||||||
|
return new AssignmentStatement(type, variable, expression);
|
||||||
|
}
|
||||||
|
throw new RuntimeException("Unknown statement");
|
||||||
|
}
|
||||||
|
|
||||||
|
private Type type() {
|
||||||
|
if (match(TokenType.INT)) return Type.INT;
|
||||||
|
if (match(TokenType.LONG)) return Type.LONG;
|
||||||
|
if (match(TokenType.DOUBLE)) return Type.DOUBLE;
|
||||||
|
if (match(TokenType.STRING)) return Type.STRING;
|
||||||
|
throw new RuntimeException("Unknown type");
|
||||||
|
}
|
||||||
|
|
||||||
|
private Expression expression() {
|
||||||
|
return additive();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Expression additive() {
|
||||||
|
Expression result = multiplicative();
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
if (match(TokenType.PLUS)) {
|
||||||
|
result = new BinaryExpression('+', result, multiplicative());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (match(TokenType.MINUS)) {
|
||||||
|
result = new BinaryExpression('-', result, multiplicative());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Expression multiplicative() {
|
||||||
|
Expression result = typeCast();
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
if (match(TokenType.STAR)) {
|
||||||
|
result = new BinaryExpression('*', result, typeCast());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (match(TokenType.SLASH)) {
|
||||||
|
result = new BinaryExpression('/', result, typeCast());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Expression typeCast() {
|
||||||
|
if (lookMatch(0, TokenType.LPAREN) && lookMatch(2, TokenType.RPAREN)) {
|
||||||
|
if (lookMatch(1, TokenType.INT) || lookMatch(1, TokenType.DOUBLE)) {
|
||||||
|
consume(TokenType.LPAREN);
|
||||||
|
final Type type = type();
|
||||||
|
consume(TokenType.RPAREN);
|
||||||
|
return new TypeCastExpression(type, unary());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return unary();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Expression unary() {
|
||||||
|
if (match(TokenType.MINUS)) {
|
||||||
|
return new UnaryExpression('-', primary());
|
||||||
|
}
|
||||||
|
if (match(TokenType.PLUS)) {
|
||||||
|
return primary();
|
||||||
|
}
|
||||||
|
return primary();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Expression primary() {
|
||||||
|
final Token current = get(0);
|
||||||
|
if (match(TokenType.NUMBER)) {
|
||||||
|
Value value;
|
||||||
|
if (current.getText().contains(".")) {
|
||||||
|
value = new DoubleValue(Double.parseDouble(current.getText()));
|
||||||
|
} else {
|
||||||
|
value = new IntValue(Integer.parseInt(current.getText()));
|
||||||
|
}
|
||||||
|
return new ValueExpression(value);
|
||||||
|
}
|
||||||
|
if (match(TokenType.HEX_NUMBER)) {
|
||||||
|
return new ValueExpression((int)Long.parseLong(current.getText(), 16));
|
||||||
|
}
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
|
||||||
|
private Token consume(TokenType type) {
|
||||||
|
final Token current = get(0);
|
||||||
|
if (type != current.getType()) throw new RuntimeException("Token " + current + " doesn't match " + type);
|
||||||
|
pos++;
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean match(TokenType type) {
|
||||||
|
final Token current = get(0);
|
||||||
|
if (type != current.getType()) return false;
|
||||||
|
pos++;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Token get(int relativePosition) {
|
||||||
|
final int position = pos + relativePosition;
|
||||||
|
if (position >= size) return EOF;
|
||||||
|
return tokens.get(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean lookMatch(int i, TokenType type) {
|
||||||
|
return get(i).getType() == type;
|
||||||
|
}
|
||||||
|
}
|
40
src/com/annimon/stylang/parser/Token.java
Normal file
40
src/com/annimon/stylang/parser/Token.java
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
package com.annimon.stylang.parser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author aNNiMON
|
||||||
|
*/
|
||||||
|
public final class Token {
|
||||||
|
|
||||||
|
private TokenType type;
|
||||||
|
private String text;
|
||||||
|
|
||||||
|
public Token() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public Token(TokenType type, String text) {
|
||||||
|
this.type = type;
|
||||||
|
this.text = text;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TokenType getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setType(TokenType type) {
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getText() {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setText(String text) {
|
||||||
|
this.text = text;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return type + " " + text;
|
||||||
|
}
|
||||||
|
}
|
61
src/com/annimon/stylang/parser/TokenType.java
Normal file
61
src/com/annimon/stylang/parser/TokenType.java
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
package com.annimon.stylang.parser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author aNNiMON
|
||||||
|
*/
|
||||||
|
public enum TokenType {
|
||||||
|
|
||||||
|
NUMBER,
|
||||||
|
HEX_NUMBER,
|
||||||
|
WORD,
|
||||||
|
TEXT,
|
||||||
|
|
||||||
|
// types
|
||||||
|
INT,
|
||||||
|
LONG,
|
||||||
|
FLOAT,
|
||||||
|
DOUBLE,
|
||||||
|
STRING,
|
||||||
|
|
||||||
|
VAR,
|
||||||
|
|
||||||
|
// keyword
|
||||||
|
PRINT,
|
||||||
|
IF,
|
||||||
|
ELSE,
|
||||||
|
WHILE,
|
||||||
|
FOR,
|
||||||
|
DO,
|
||||||
|
BREAK,
|
||||||
|
CONTINUE,
|
||||||
|
DEF,
|
||||||
|
RETURN,
|
||||||
|
|
||||||
|
PLUS,
|
||||||
|
MINUS,
|
||||||
|
STAR,
|
||||||
|
SLASH,
|
||||||
|
EQ,
|
||||||
|
EQEQ,
|
||||||
|
EXCL,
|
||||||
|
EXCLEQ,
|
||||||
|
LT,
|
||||||
|
LTEQ,
|
||||||
|
GT,
|
||||||
|
GTEQ,
|
||||||
|
|
||||||
|
BAR,
|
||||||
|
BARBAR,
|
||||||
|
AMP,
|
||||||
|
AMPAMP,
|
||||||
|
|
||||||
|
LPAREN, // (
|
||||||
|
RPAREN, // )
|
||||||
|
LBRACE, // {
|
||||||
|
RBRACE, // }
|
||||||
|
COMMA, // ,
|
||||||
|
SEMICOLON, // ;
|
||||||
|
|
||||||
|
EOF
|
||||||
|
}
|
40
src/com/annimon/stylang/parser/ast/AssignmentStatement.java
Normal file
40
src/com/annimon/stylang/parser/ast/AssignmentStatement.java
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
package com.annimon.stylang.parser.ast;
|
||||||
|
|
||||||
|
import com.annimon.stylang.lib.Type;
|
||||||
|
import com.annimon.stylang.lib.Value;
|
||||||
|
import com.annimon.stylang.lib.Variables;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author aNNiMON
|
||||||
|
*/
|
||||||
|
public final class AssignmentStatement implements Statement {
|
||||||
|
|
||||||
|
public final Type type;
|
||||||
|
public final String variable;
|
||||||
|
public final Expression expression;
|
||||||
|
|
||||||
|
public AssignmentStatement(Type type, String variable, Expression expression) {
|
||||||
|
this.type = type;
|
||||||
|
this.variable = variable;
|
||||||
|
this.expression = expression;
|
||||||
|
Variables.setType(variable, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute() {
|
||||||
|
Variables.set(variable, expression.eval());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void accept(Visitor visitor) {
|
||||||
|
visitor.start(this);
|
||||||
|
expression.accept(visitor);
|
||||||
|
visitor.finish(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return String.format("%s = %s", variable, expression);
|
||||||
|
}
|
||||||
|
}
|
113
src/com/annimon/stylang/parser/ast/BinaryExpression.java
Normal file
113
src/com/annimon/stylang/parser/ast/BinaryExpression.java
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
package com.annimon.stylang.parser.ast;
|
||||||
|
|
||||||
|
import com.annimon.stylang.lib.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author aNNiMON
|
||||||
|
*/
|
||||||
|
public final class BinaryExpression implements Expression {
|
||||||
|
|
||||||
|
public final Expression expr1, expr2;
|
||||||
|
public final char operation;
|
||||||
|
|
||||||
|
public BinaryExpression(char operation, Expression expr1, Expression expr2) {
|
||||||
|
this.operation = operation;
|
||||||
|
this.expr1 = expr1;
|
||||||
|
this.expr2 = expr2;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Type type() {
|
||||||
|
final Type t1 = expr1.type();
|
||||||
|
final Type t2 = expr2.type();
|
||||||
|
switch (operation) {
|
||||||
|
case '+':
|
||||||
|
if (t1 == Type.STRING) return Type.STRING;
|
||||||
|
if (t1 == Type.INT && t2 == Type.INT) return Type.INT;
|
||||||
|
if (t1 == Type.DOUBLE && t2 == Type.INT) return Type.DOUBLE;
|
||||||
|
if (t1 == Type.DOUBLE && t2 == Type.DOUBLE) return Type.DOUBLE;
|
||||||
|
break;
|
||||||
|
case '-':
|
||||||
|
case '*':
|
||||||
|
case '/':
|
||||||
|
if (t1 == Type.INT && t2 == Type.INT) return Type.INT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
throw new RuntimeException("Unsupported types in operation");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Value eval() {
|
||||||
|
final Value value1 = expr1.eval();
|
||||||
|
final Value value2 = expr2.eval();
|
||||||
|
switch (operation) {
|
||||||
|
case '+': return plus(value1, value2);
|
||||||
|
case '-': return minus(value1, value2);
|
||||||
|
case '*': return multiply(value1, value2);
|
||||||
|
case '/': return divide(value1, value2);
|
||||||
|
}
|
||||||
|
throw new UnsupportedOperationException("Unknown operation");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void accept(Visitor visitor) {
|
||||||
|
visitor.start(this);
|
||||||
|
expr1.accept(visitor);
|
||||||
|
visitor.visit(this);
|
||||||
|
expr2.accept(visitor);
|
||||||
|
visitor.finish(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Value plus(Value value1, Value value2) {
|
||||||
|
if (value1.type() == Type.INT) {
|
||||||
|
if (value2.type() == Type.INT) {
|
||||||
|
return new IntValue(IntValue.get(value1) + IntValue.get(value2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (value1.type() == Type.DOUBLE) {
|
||||||
|
if (value2.type() == Type.INT) {
|
||||||
|
return new DoubleValue(DoubleValue.get(value1) + IntValue.get(value2));
|
||||||
|
}
|
||||||
|
if (value2.type() == Type.DOUBLE) {
|
||||||
|
return new DoubleValue(DoubleValue.get(value1) + DoubleValue.get(value2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (value1.type() == Type.STRING) {
|
||||||
|
return new StringValue(StringValue.get(value1) + value2.toString());
|
||||||
|
}
|
||||||
|
throw new UnsupportedOperationException("plus in unsupported type " + value1.type() + " and " + value2.type());
|
||||||
|
}
|
||||||
|
|
||||||
|
private Value minus(Value value1, Value value2) {
|
||||||
|
if (value1.type() == Type.INT) {
|
||||||
|
if (value2.type() == Type.INT) {
|
||||||
|
return new IntValue(IntValue.get(value1) - IntValue.get(value2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new UnsupportedOperationException("minus in unsupported type " + value1.type() + " and " + value2.type());
|
||||||
|
}
|
||||||
|
|
||||||
|
private Value multiply(Value value1, Value value2) {
|
||||||
|
if (value1.type() == Type.INT) {
|
||||||
|
if (value2.type() == Type.INT) {
|
||||||
|
return new IntValue(IntValue.get(value1) * IntValue.get(value2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new UnsupportedOperationException("multiply onn unsupported type " + value1.type() + " and " + value2.type());
|
||||||
|
}
|
||||||
|
|
||||||
|
private Value divide(Value value1, Value value2) {
|
||||||
|
if (value1.type() == Type.INT) {
|
||||||
|
if (value2.type() == Type.INT) {
|
||||||
|
return new IntValue(IntValue.get(value1) / IntValue.get(value2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new UnsupportedOperationException("divide on unsupported type " + value1.type() + " and " + value2.type());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return String.format("[%s %c %s]", expr1, operation, expr2);
|
||||||
|
}
|
||||||
|
}
|
46
src/com/annimon/stylang/parser/ast/BlockStatement.java
Normal file
46
src/com/annimon/stylang/parser/ast/BlockStatement.java
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
package com.annimon.stylang.parser.ast;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author aNNiMON
|
||||||
|
*/
|
||||||
|
public final class BlockStatement implements Statement {
|
||||||
|
|
||||||
|
public final List<Statement> statements;
|
||||||
|
|
||||||
|
public BlockStatement() {
|
||||||
|
statements = new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(Statement statement) {
|
||||||
|
statements.add(statement);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute() {
|
||||||
|
for (Statement statement : statements) {
|
||||||
|
statement.execute();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void accept(Visitor visitor) {
|
||||||
|
visitor.start(this);
|
||||||
|
for (Statement statement : statements) {
|
||||||
|
statement.accept(visitor);
|
||||||
|
}
|
||||||
|
visitor.finish(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
final StringBuilder result = new StringBuilder();
|
||||||
|
for (Statement statement : statements) {
|
||||||
|
result.append(statement.toString()).append(System.lineSeparator());
|
||||||
|
}
|
||||||
|
return result.toString();
|
||||||
|
}
|
||||||
|
}
|
15
src/com/annimon/stylang/parser/ast/Expression.java
Normal file
15
src/com/annimon/stylang/parser/ast/Expression.java
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
package com.annimon.stylang.parser.ast;
|
||||||
|
|
||||||
|
import com.annimon.stylang.lib.Type;
|
||||||
|
import com.annimon.stylang.lib.Value;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author aNNiMON
|
||||||
|
*/
|
||||||
|
public interface Expression extends Node {
|
||||||
|
|
||||||
|
Type type();
|
||||||
|
|
||||||
|
Value eval();
|
||||||
|
}
|
10
src/com/annimon/stylang/parser/ast/Node.java
Normal file
10
src/com/annimon/stylang/parser/ast/Node.java
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
package com.annimon.stylang.parser.ast;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author aNNiMON
|
||||||
|
*/
|
||||||
|
public interface Node {
|
||||||
|
|
||||||
|
void accept(Visitor visitor);
|
||||||
|
}
|
31
src/com/annimon/stylang/parser/ast/PrintStatement.java
Normal file
31
src/com/annimon/stylang/parser/ast/PrintStatement.java
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
package com.annimon.stylang.parser.ast;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author aNNiMON
|
||||||
|
*/
|
||||||
|
public final class PrintStatement implements Statement {
|
||||||
|
|
||||||
|
public final Expression expression;
|
||||||
|
|
||||||
|
public PrintStatement(Expression expression) {
|
||||||
|
this.expression = expression;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute() {
|
||||||
|
System.out.println(expression.eval());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void accept(Visitor visitor) {
|
||||||
|
visitor.start(this);
|
||||||
|
expression.accept(visitor);
|
||||||
|
visitor.finish(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "print " + expression;
|
||||||
|
}
|
||||||
|
}
|
10
src/com/annimon/stylang/parser/ast/Statement.java
Normal file
10
src/com/annimon/stylang/parser/ast/Statement.java
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
package com.annimon.stylang.parser.ast;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author aNNiMON
|
||||||
|
*/
|
||||||
|
public interface Statement extends Node {
|
||||||
|
|
||||||
|
void execute();
|
||||||
|
}
|
73
src/com/annimon/stylang/parser/ast/TypeCastExpression.java
Normal file
73
src/com/annimon/stylang/parser/ast/TypeCastExpression.java
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
package com.annimon.stylang.parser.ast;
|
||||||
|
|
||||||
|
import com.annimon.stylang.lib.DoubleValue;
|
||||||
|
import com.annimon.stylang.lib.IntValue;
|
||||||
|
import com.annimon.stylang.lib.Type;
|
||||||
|
import com.annimon.stylang.lib.Value;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author aNNiMON
|
||||||
|
*/
|
||||||
|
public final class TypeCastExpression implements Expression {
|
||||||
|
|
||||||
|
public enum Direction {
|
||||||
|
NONE,
|
||||||
|
I2D,
|
||||||
|
D2I,
|
||||||
|
|
||||||
|
CUSTOM,
|
||||||
|
INVALID,
|
||||||
|
}
|
||||||
|
|
||||||
|
public final Type type;
|
||||||
|
public final Expression expression;
|
||||||
|
public final Direction direction;
|
||||||
|
|
||||||
|
public TypeCastExpression(Type type, Expression expression) {
|
||||||
|
this.type = type;
|
||||||
|
this.expression = expression;
|
||||||
|
direction = getDirection();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Direction getDirection() {
|
||||||
|
final Type from = expression.type();
|
||||||
|
final Type to = type;
|
||||||
|
if (from == to) return Direction.NONE;
|
||||||
|
if (from == Type.INT) {
|
||||||
|
if (to == Type.DOUBLE) return Direction.I2D;
|
||||||
|
}
|
||||||
|
if (from == Type.DOUBLE) {
|
||||||
|
if (to == Type.INT) return Direction.D2I;
|
||||||
|
}
|
||||||
|
return Direction.INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Type type() {
|
||||||
|
if (direction == Direction.INVALID) throw new RuntimeException("Invalid type cast");
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Value eval() {
|
||||||
|
final Value result = expression.eval();
|
||||||
|
switch (direction) {
|
||||||
|
case NONE: return result;
|
||||||
|
case I2D: return new DoubleValue((double) IntValue.get(result));
|
||||||
|
case D2I: return new IntValue((int) DoubleValue.get(result));
|
||||||
|
}
|
||||||
|
throw new RuntimeException("Unsupported type cast from " + result.type() + " to " + type);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void accept(Visitor visitor) {
|
||||||
|
expression.accept(visitor);
|
||||||
|
visitor.visit(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "(" + type + ")" + expression;
|
||||||
|
}
|
||||||
|
}
|
65
src/com/annimon/stylang/parser/ast/UnaryExpression.java
Normal file
65
src/com/annimon/stylang/parser/ast/UnaryExpression.java
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
package com.annimon.stylang.parser.ast;
|
||||||
|
|
||||||
|
import com.annimon.stylang.lib.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author aNNiMON
|
||||||
|
*/
|
||||||
|
public final class UnaryExpression implements Expression {
|
||||||
|
|
||||||
|
public final char operation;
|
||||||
|
private final Expression expr1;
|
||||||
|
|
||||||
|
public UnaryExpression(char operation, Expression expr1) {
|
||||||
|
this.operation = operation;
|
||||||
|
this.expr1 = expr1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Type type() {
|
||||||
|
final Type t1 = expr1.type();
|
||||||
|
switch (operation) {
|
||||||
|
case '+':
|
||||||
|
case '-':
|
||||||
|
if (t1 == Type.INT) return Type.INT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
throw new RuntimeException("Unsupported types in operation");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Value eval() {
|
||||||
|
switch (operation) {
|
||||||
|
case '-': return minus(expr1.eval());
|
||||||
|
case '+':
|
||||||
|
return plus(expr1.eval());
|
||||||
|
}
|
||||||
|
throw new UnsupportedOperationException("Unknown operation");
|
||||||
|
}
|
||||||
|
|
||||||
|
private Value plus(Value value) {
|
||||||
|
if (value.type() == Type.INT) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
throw new UnsupportedOperationException("plus in unsupported type");
|
||||||
|
}
|
||||||
|
|
||||||
|
private Value minus(Value value) {
|
||||||
|
if (value.type() == Type.INT) {
|
||||||
|
return new IntValue(-IntValue.get(value));
|
||||||
|
}
|
||||||
|
throw new UnsupportedOperationException("minus in unsupported type");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void accept(Visitor visitor) {
|
||||||
|
visitor.visit(this);
|
||||||
|
expr1.accept(visitor);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return String.format("%c %s", operation, expr1);
|
||||||
|
}
|
||||||
|
}
|
48
src/com/annimon/stylang/parser/ast/ValueExpression.java
Normal file
48
src/com/annimon/stylang/parser/ast/ValueExpression.java
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
package com.annimon.stylang.parser.ast;
|
||||||
|
|
||||||
|
import com.annimon.stylang.lib.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author aNNiMON
|
||||||
|
*/
|
||||||
|
public final class ValueExpression implements Expression {
|
||||||
|
|
||||||
|
public final Value value;
|
||||||
|
|
||||||
|
public ValueExpression(int value) {
|
||||||
|
this.value = new IntValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ValueExpression(double value) {
|
||||||
|
this.value = new DoubleValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ValueExpression(String value) {
|
||||||
|
this.value = new StringValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ValueExpression(Value value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Type type() {
|
||||||
|
return value.type();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Value eval() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void accept(Visitor visitor) {
|
||||||
|
visitor.visit(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return value.toString();
|
||||||
|
}
|
||||||
|
}
|
39
src/com/annimon/stylang/parser/ast/VariableExpression.java
Normal file
39
src/com/annimon/stylang/parser/ast/VariableExpression.java
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
package com.annimon.stylang.parser.ast;
|
||||||
|
|
||||||
|
import com.annimon.stylang.lib.Type;
|
||||||
|
import com.annimon.stylang.lib.Value;
|
||||||
|
import com.annimon.stylang.lib.Variables;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author aNNiMON
|
||||||
|
*/
|
||||||
|
public final class VariableExpression implements Expression {
|
||||||
|
|
||||||
|
public final String variable;
|
||||||
|
|
||||||
|
public VariableExpression(String name) {
|
||||||
|
this.variable = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Type type() {
|
||||||
|
return Variables.getType(variable);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Value eval() {
|
||||||
|
return Variables.get(variable);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void accept(Visitor visitor) {
|
||||||
|
visitor.visit(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
// return String.format("%s [%f]", name, Constants.get(name));
|
||||||
|
return String.format("%s", variable);
|
||||||
|
}
|
||||||
|
}
|
34
src/com/annimon/stylang/parser/ast/Visitor.java
Normal file
34
src/com/annimon/stylang/parser/ast/Visitor.java
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
package com.annimon.stylang.parser.ast;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author aNNiMON
|
||||||
|
*/
|
||||||
|
public interface Visitor {
|
||||||
|
|
||||||
|
void start(AssignmentStatement assignment);
|
||||||
|
|
||||||
|
void finish(AssignmentStatement assignment);
|
||||||
|
|
||||||
|
void start(BinaryExpression expr);
|
||||||
|
|
||||||
|
void visit(BinaryExpression expr);
|
||||||
|
|
||||||
|
void finish(BinaryExpression expr);
|
||||||
|
|
||||||
|
void start(BlockStatement block);
|
||||||
|
|
||||||
|
void finish(BlockStatement block);
|
||||||
|
|
||||||
|
void start(PrintStatement statement);
|
||||||
|
|
||||||
|
void finish(PrintStatement statement);
|
||||||
|
|
||||||
|
void visit(TypeCastExpression expr);
|
||||||
|
|
||||||
|
void visit(UnaryExpression expr);
|
||||||
|
|
||||||
|
void visit(ValueExpression v);
|
||||||
|
|
||||||
|
void visit(VariableExpression ve);
|
||||||
|
}
|
170
src/com/annimon/stylang/parser/visitors/ClassCompiler.java
Normal file
170
src/com/annimon/stylang/parser/visitors/ClassCompiler.java
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
package com.annimon.stylang.parser.visitors;
|
||||||
|
|
||||||
|
import com.annimon.stylang.lib.*;
|
||||||
|
import com.annimon.stylang.parser.ast.*;
|
||||||
|
import org.objectweb.asm.ClassWriter;
|
||||||
|
import org.objectweb.asm.MethodVisitor;
|
||||||
|
import static org.objectweb.asm.Opcodes.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author aNNiMON
|
||||||
|
*/
|
||||||
|
public final class ClassCompiler extends VisitorAdapter {
|
||||||
|
|
||||||
|
private String className;
|
||||||
|
private ClassWriter cw;
|
||||||
|
private MethodVisitor mw;
|
||||||
|
|
||||||
|
public byte[] compile(Node node) {
|
||||||
|
cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
|
||||||
|
// public class Main
|
||||||
|
className = "Main";
|
||||||
|
cw.visit(V1_5, ACC_PUBLIC, className, null, "java/lang/Object", null);
|
||||||
|
|
||||||
|
// Конструктор public Main() {}
|
||||||
|
final MethodVisitor constructor = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
|
||||||
|
// pushes the 'this' variable
|
||||||
|
constructor.visitVarInsn(ALOAD, 0);
|
||||||
|
// invokes the super class constructor
|
||||||
|
constructor.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
|
||||||
|
constructor.visitInsn(RETURN);
|
||||||
|
// this code uses a maximum of one stack element and one local variable
|
||||||
|
constructor.visitMaxs(1, 1);
|
||||||
|
constructor.visitEnd();
|
||||||
|
|
||||||
|
// public static void main(String[] args) { }
|
||||||
|
mw = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, "main", "([Ljava/lang/String;)V", null, null);
|
||||||
|
|
||||||
|
// Компилируем код
|
||||||
|
node.accept(this);
|
||||||
|
|
||||||
|
mw.visitInsn(RETURN);
|
||||||
|
// max stack and max locals automatically computed
|
||||||
|
mw.visitMaxs(0, 0);
|
||||||
|
mw.visitEnd();
|
||||||
|
|
||||||
|
return cw.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void start(AssignmentStatement assignment) {
|
||||||
|
cw.visitField(ACC_PRIVATE | ACC_STATIC, assignment.variable, typeToJavaType(assignment.type), null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void finish(AssignmentStatement assignment) {
|
||||||
|
mw.visitFieldInsn(PUTSTATIC, className, assignment.variable, typeToJavaType(assignment.type));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void start(BinaryExpression expr) {
|
||||||
|
if (expr.operation == '+' && expr.type() == Type.STRING) {
|
||||||
|
// Конкатенация строк - начало
|
||||||
|
mw.visitTypeInsn(NEW, "java/lang/StringBuilder");
|
||||||
|
mw.visitInsn(DUP);
|
||||||
|
mw.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "()V", false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(BinaryExpression expr) {
|
||||||
|
if (expr.operation == '+' && expr.type() == Type.STRING) {
|
||||||
|
// Конкатенация строк - середина
|
||||||
|
// Записали первый аргумент, теперь вызывает append(..)
|
||||||
|
mw.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void finish(BinaryExpression expr) {
|
||||||
|
final Type type = expr.type();
|
||||||
|
switch (expr.operation) {
|
||||||
|
case '+': {
|
||||||
|
if (type == Type.INT) mw.visitInsn(IADD);
|
||||||
|
if (type == Type.DOUBLE) mw.visitInsn(DADD);
|
||||||
|
if (type == Type.STRING) {
|
||||||
|
// Конкатенация строк - завершение
|
||||||
|
// Записали второй аргумент, теперь вызывает append(..) и toString
|
||||||
|
final String signature = "("+ typeToJavaType(expr.expr2.type()) + ")Ljava/lang/StringBuilder;";
|
||||||
|
mw.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", signature, false);
|
||||||
|
mw.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;", false);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case '-': {
|
||||||
|
if (type == Type.INT) mw.visitInsn(ISUB);
|
||||||
|
if (type == Type.DOUBLE) mw.visitInsn(DSUB);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case '*': {
|
||||||
|
if (type == Type.INT) mw.visitInsn(IMUL);
|
||||||
|
if (type == Type.DOUBLE) mw.visitInsn(DMUL);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case '/': {
|
||||||
|
if (type == Type.INT) mw.visitInsn(IDIV);
|
||||||
|
if (type == Type.DOUBLE) mw.visitInsn(DDIV);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void start(BlockStatement block) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void finish(BlockStatement block) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void start(PrintStatement statement) {
|
||||||
|
// pushes the 'out' field (of type PrintStream) of the System class
|
||||||
|
mw.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void finish(PrintStatement statement) {
|
||||||
|
// invokes the 'println' method (defined in the PrintStream class)
|
||||||
|
final String signature = "("+ typeToJavaType(statement.expression.type()) + ")V";
|
||||||
|
mw.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", signature, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(TypeCastExpression expr) {
|
||||||
|
switch (expr.direction) {
|
||||||
|
case D2I: mw.visitInsn(D2I);
|
||||||
|
case I2D: mw.visitInsn(I2D);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(UnaryExpression expr) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(ValueExpression v) {
|
||||||
|
mw.visitLdcInsn(v.value.raw());
|
||||||
|
/*final Type type = v.type();
|
||||||
|
if (type == Type.INT) {
|
||||||
|
mw.visitLdcInsn(IntValue.get(v.value));
|
||||||
|
} else if (type == Type.DOUBLE) {
|
||||||
|
mw.visitLdcInsn(DoubleValue.get(v.value));
|
||||||
|
} else if (type == Type.STRING) {
|
||||||
|
mw.visitLdcInsn(StringValue.get(v.value));
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(VariableExpression ve) {
|
||||||
|
mw.visitFieldInsn(GETSTATIC, className, ve.variable, typeToJavaType(ve.type()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private String typeToJavaType(Type type) {
|
||||||
|
if (type == Type.INT) return "I";
|
||||||
|
if (type == Type.DOUBLE) return "D";
|
||||||
|
if (type == Type.STRING) return "Ljava/lang/String;";
|
||||||
|
return "java/lang/Object";
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
package com.annimon.stylang.parser.visitors;
|
||||||
|
|
||||||
|
import com.annimon.stylang.parser.ast.ValueExpression;
|
||||||
|
import com.annimon.stylang.parser.ast.VariableExpression;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author aNNiMON
|
||||||
|
*/
|
||||||
|
public final class ConstantPoolPrinter extends VisitorAdapter {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(ValueExpression v) {
|
||||||
|
super.visit(v);
|
||||||
|
System.out.println(v.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(VariableExpression ve) {
|
||||||
|
super.visit(ve);
|
||||||
|
System.out.println(ve.variable);
|
||||||
|
}
|
||||||
|
}
|
59
src/com/annimon/stylang/parser/visitors/VisitorAdapter.java
Normal file
59
src/com/annimon/stylang/parser/visitors/VisitorAdapter.java
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
package com.annimon.stylang.parser.visitors;
|
||||||
|
|
||||||
|
import com.annimon.stylang.parser.ast.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author aNNiMON
|
||||||
|
*/
|
||||||
|
public class VisitorAdapter implements Visitor {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void start(AssignmentStatement assignment) {
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void finish(AssignmentStatement assignment) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void start(BinaryExpression expr) {
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void visit(BinaryExpression expr) {
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void finish(BinaryExpression expr) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void start(BlockStatement block) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void finish(BlockStatement block) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void start(PrintStatement statement) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void finish(PrintStatement statement) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(TypeCastExpression expr) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(UnaryExpression expr) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(ValueExpression v) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(VariableExpression ve) {
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user