commit 85bcb906edabd087c149827af0a3a5018494598f Author: Jörg Prante Date: Sat Oct 22 23:50:38 2016 +0200 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..644a0f3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +/data +/work +/logs +/.idea +/target +.DS_Store +/.settings +/.classpath +/.project +/.gradle +/build +*~ \ No newline at end of file diff --git a/LICENSE-jacc.txt b/LICENSE-jacc.txt new file mode 100644 index 0000000..4017c4b --- /dev/null +++ b/LICENSE-jacc.txt @@ -0,0 +1,33 @@ +Copyright (c) 1999-2004, Mark P Jones, Oregon Health & Science +University (OGI School of Science & Engineering at OHSU) +All rights reserved. + +Redistribution and use in source and binary forms, with or +without modification, are permitted provided that the following +conditions are met: + + o Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + o Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + + o Neither the name of the Oregon Health & Science University, the + OGI School of Science & Engineering at OHSU nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..b511086 --- /dev/null +++ b/build.gradle @@ -0,0 +1,45 @@ +plugins { + id "org.sonarqube" version '2.1-rc1' +} + +ext { + user = 'jprante' + name = 'jacc' + description = 'Java implementation of Yet Another Compiler-Compiler' + scmUrl = 'https://github.com/' + user + '/' + name + scmConnection = 'scm:git:git://github.com/' + user + '/' + name + '.git' + scmDeveloperConnection = 'scm:git:git://github.com/' + user + '/' + name + '.git' +} + +group = 'org.xbib' +version = '2.1.0' + +apply plugin: 'java' +apply plugin: 'maven' +apply plugin: 'signing' +apply plugin: 'findbugs' +apply plugin: 'pmd' +apply plugin: 'checkstyle' +apply plugin: 'jacoco' + +sourceCompatibility = 1.8 +targetCompatibility = 1.8 + +repositories { + mavenCentral() + maven { + url "http://xbib.org/repository" + } +} + +configurations { + wagon +} + +dependencies { + testCompile 'junit:junit:4.12' + wagon 'org.apache.maven.wagon:wagon-ssh-external:2.10' +} + +apply from: 'gradle/publish.gradle' +apply from: 'gradle/sonarqube.gradle' diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml new file mode 100644 index 0000000..52fe33c --- /dev/null +++ b/config/checkstyle/checkstyle.xml @@ -0,0 +1,323 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/gradle/publish.gradle b/gradle/publish.gradle new file mode 100644 index 0000000..951f131 --- /dev/null +++ b/gradle/publish.gradle @@ -0,0 +1,104 @@ + +task xbibUpload(type: Upload) { + configuration = configurations.archives + uploadDescriptor = true + repositories { + if (project.hasProperty("xbibUsername")) { + mavenDeployer { + configuration = configurations.wagon + repository(url: 'scpexe://xbib.org/repository') { + authentication(userName: xbibUsername, privateKey: xbibPrivateKey) + } + } + } + } +} + +task sonaTypeUpload(type: Upload) { + configuration = configurations.archives + uploadDescriptor = true + repositories { + if (project.hasProperty('ossrhUsername')) { + mavenDeployer { + beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } + repository(url: 'https://oss.sonatype.org/service/local/staging/deploy/maven2') { + authentication(userName: ossrhUsername, password: ossrhPassword) + } + snapshotRepository(url: 'https://oss.sonatype.org/content/repositories/snapshots') { + authentication(userName: ossrhUsername, password: ossrhPassword) + } + pom.project { + name name + description description + packaging 'jar' + inceptionYear '2012' + url scmUrl + organization { + name 'xbib' + url 'http://xbib.org' + } + developers { + developer { + id user + name 'Jörg Prante' + email 'joergprante@gmail.com' + url 'https://github.com/jprante' + } + } + scm { + url scmUrl + connection scmConnection + developerConnection scmDeveloperConnection + } + licenses { + license { + name 'The Apache License, Version 2.0' + url 'http://www.apache.org/licenses/LICENSE-2.0.txt' + } + } + } + } + } + } +} + + +task hbzUpload(type: Upload) { + configuration = configurations.archives + uploadDescriptor = true + repositories { + if (project.hasProperty('hbzUserName')) { + mavenDeployer { + configuration = configurations.wagon + beforeDeployment { MavenDeployment deployment -> + signing.signPom(deployment) + } + repository(url: uri(hbzUrl)) { + authentication(userName: hbzUserName, privateKey: hbzPrivateKey) + } + pom.project { + developers { + developer { + id 'jprante' + name 'Jörg Prante' + email 'joergprante@gmail.com' + url 'https://github.com/jprante' + } + } + scm { + url 'https://github.com/xbib/groovy-webapp' + connection 'scm:git:git://github.com/xbib/groovy-webapp.git' + developerConnection 'scm:git:git://github.com/xbib/groovy-webapp.git' + } + inceptionYear '2016' + licenses { + license { + name 'The Apache License, Version 2.0' + url 'http://www.apache.org/licenses/LICENSE-2.0.txt' + } + } + } + } + } + } +} diff --git a/gradle/sonarqube.gradle b/gradle/sonarqube.gradle new file mode 100644 index 0000000..6230b16 --- /dev/null +++ b/gradle/sonarqube.gradle @@ -0,0 +1,42 @@ +tasks.withType(Checkstyle) { + ignoreFailures = true + reports { + xml.enabled = true + html.enabled = true + } +} +tasks.withType(Pmd) { + ignoreFailures = true + reports { + xml.enabled = true + html.enabled = true + } +} +tasks.withType(FindBugs) { + ignoreFailures = true + reports { + xml.enabled = true + html.enabled = false + } +} +jacocoTestReport { + reports { + xml.enabled true + csv.enabled false + xml.destination "${buildDir}/reports/jacoco-xml" + html.destination "${buildDir}/reports/jacoco-html" + } +} + +sonarqube { + properties { + property "sonar.projectName", "jacc" + property "sonar.sourceEncoding", "UTF-8" + property "sonar.language", "java" + property "sonar.sources", "src/main/java" + property "sonar.tests", "src/test/java" + property "sonar.scm.provider", "git" + property "sonar.java.coveragePlugin", "jacoco" + property "sonar.junit.reportsPath", "build/test-results/test/" + } +} diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..6ffa237 Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..2267e55 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Sat Oct 22 21:01:39 CEST 2016 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-3.1-bin.zip diff --git a/gradlew b/gradlew new file mode 100755 index 0000000..9aa616c --- /dev/null +++ b/gradlew @@ -0,0 +1,169 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [[ "$(uname)" == "Darwin" ]] && [[ "$HOME" == "$PWD" ]]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..e95643d --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,84 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..91c0264 --- /dev/null +++ b/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'jacc' diff --git a/src/main/java/org/xbib/jacc/Conflicts.java b/src/main/java/org/xbib/jacc/Conflicts.java new file mode 100644 index 0000000..8460674 --- /dev/null +++ b/src/main/java/org/xbib/jacc/Conflicts.java @@ -0,0 +1,77 @@ +package org.xbib.jacc; + +import org.xbib.jacc.grammar.Grammar; +import org.xbib.jacc.grammar.Machine; + +/** + * + */ +class Conflicts { + + private int type; + private int arg1; + private int arg2; + private Grammar.Symbol sym; + private Conflicts next; + + private Conflicts(int i, int j, int k, Grammar.Symbol symbol, Conflicts conflicts) { + type = i; + arg1 = j; + arg2 = k; + sym = symbol; + next = conflicts; + } + + static Conflicts sr(int i, int j, Grammar.Symbol symbol, Conflicts conflicts) { + return append(conflicts, new Conflicts(0, i, j, symbol, null)); + } + + static Conflicts rr(int i, int j, Grammar.Symbol symbol, Conflicts conflicts) { + return append(conflicts, new Conflicts(1, i, j, symbol, null)); + } + + private static Conflicts append(Conflicts conflicts, Conflicts conflicts1) { + if (conflicts == null) { + return conflicts1; + } + Conflicts conflicts2; + conflicts2 = conflicts; + while (conflicts2.next != null) { + conflicts2 = conflicts2.next; + } + conflicts2.next = conflicts1; + return conflicts; + } + + static String describe(Machine machine, int i, Conflicts conflicts) { + if (conflicts == null) { + return ""; + } + StringBuilder sb = new StringBuilder(); + String s = System.getProperty("line.separator", "\n"); + for (; conflicts != null; conflicts = conflicts.next) { + sb.append(i); + sb.append(": "); + if (conflicts.type == 0) { + sb.append("shift/reduce conflict ("); + if (conflicts.arg1 < 0) { + sb.append("$end"); + } else { + sb.append("shift "); + sb.append(conflicts.arg1); + } + sb.append(" and red'n "); + sb.append(machine.reduceItem(i, conflicts.arg2).getSeqNo()); + } else { + sb.append("reduce/reduce conflict (red'ns "); + sb.append(machine.reduceItem(i, conflicts.arg1).getSeqNo()); + sb.append(" and "); + sb.append(machine.reduceItem(i, conflicts.arg2).getSeqNo()); + } + sb.append(") on "); + sb.append(conflicts.sym.getName()); + sb.append(s); + } + return sb.toString(); + } +} diff --git a/src/main/java/org/xbib/jacc/Fixity.java b/src/main/java/org/xbib/jacc/Fixity.java new file mode 100644 index 0000000..fc0f81b --- /dev/null +++ b/src/main/java/org/xbib/jacc/Fixity.java @@ -0,0 +1,58 @@ +package org.xbib.jacc; + +class Fixity { + + private static final int LEFT = 1; + private static final int NONASS = 2; + private static final int RIGHT = 3; + private int assoc; + private int prec; + + private Fixity(int i, int j) { + assoc = i; + prec = j; + } + + public static Fixity left(int i) { + return new Fixity(LEFT, i); + } + + static Fixity nonass(int i) { + return new Fixity(NONASS, i); + } + + static Fixity right(int i) { + return new Fixity(RIGHT, i); + } + + static int which(Fixity fixity, Fixity fixity1) { + if (fixity != null && fixity1 != null) { + if (fixity.prec > fixity1.prec) { + return LEFT; + } + if (fixity.prec < fixity1.prec) { + return RIGHT; + } + if (fixity.assoc == LEFT && fixity1.assoc == LEFT) { + return LEFT; + } + if (fixity.assoc == RIGHT && fixity1.assoc == RIGHT) { + return RIGHT; + } + } + return NONASS; + } + + boolean equalsFixity(Fixity fixity) { + return assoc == fixity.assoc && prec == fixity.prec; + } + + @Override + public boolean equals(Object obj) { + return obj instanceof Fixity && equalsFixity((Fixity) obj); + } + + public int hashCode() { + return assoc ^ prec; + } +} diff --git a/src/main/java/org/xbib/jacc/Jacc.java b/src/main/java/org/xbib/jacc/Jacc.java new file mode 100644 index 0000000..2fd56f5 --- /dev/null +++ b/src/main/java/org/xbib/jacc/Jacc.java @@ -0,0 +1,201 @@ +package org.xbib.jacc; + +import org.xbib.jacc.compiler.ConsoleHandler; + +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.Writer; +import java.nio.charset.StandardCharsets; + +/** + * + */ +public class Jacc { + + public Jacc() { + } + + public static void main(String[] args) throws Exception { + NameList namelist = null; + String s = ".jacc"; + Settings settings = new Settings(); + boolean flag = true; + boolean flag1 = true; + boolean flag2 = false; + boolean flag4 = false; + NameList namelist1 = null; + NameList namelist2 = null; + boolean flag5 = false; + String dir = null; + Writer writer = new BufferedWriter(new OutputStreamWriter(System.out, StandardCharsets.UTF_8)); + label0: + for (int i = 0; i < args.length; i++) { + String s1 = args[i]; + if (s1.startsWith("-")) { + if (s1.length() == 1) { + usage("Missing command line options"); + } + int j = 1; + do { + if (j >= s1.length()) { + continue label0; + } + switch (s1.charAt(j)) { + case 102: // 'f' + flag4 = true; + break; + + case 112: // 'p' + flag = false; + break; + + case 116: // 't' + flag1 = false; + break; + + case 118: // 'v' + flag2 = true; + break; + + case 48: // '0' + settings.setMachineType(0); + break; + + case 115: // 's' + settings.setMachineType(1); + break; + + case 97: // 'a' + settings.setMachineType(2); + break; + + case 101: // 'e' + if (i + 1 >= args.length) { + usage("Missing filename for -e option"); + } + namelist1 = new NameList(args[++i], namelist1); + break; + + case 114: // 'r' + if (i + 1 >= args.length) { + usage("Missing filename for -r option"); + } + namelist2 = new NameList(args[++i], namelist2); + break; + + case 110: // 'n' + flag5 = true; + break; + + case 'd': + if (i + 1 >= args.length) { + usage("Missing directory for -d option"); + } + dir = args[++i]; + break; + + default: + usage("Unrecognized command line option " + s1.charAt(j)); + break; + } + j++; + } while (true); + } + if (!s1.endsWith(s)) { + usage("Input file must have \"" + s + "\" suffix"); + } else { + namelist = new NameList(s1, namelist); + } + } + + if (namelist == null) { + usage("No input file(s) specified"); + } + ConsoleHandler simplehandler = new ConsoleHandler(); + String s2 = namelist.getFirst(); + int k = 1 + Math.max(s2.lastIndexOf('\\'), s2.lastIndexOf('/')); + dir = dir == null ? s2.substring(0, k) : dir; + String s4 = s2.substring(k, s2.length() - s.length()); + final JaccJob job = new JaccJob(simplehandler, writer, settings); + NameList.visit(namelist, new NameList.Visitor() { + public void visit(String s5) throws IOException { + job.parseGrammarFile(s5); + } + }); + job.buildTables(); + settings.fillBlanks(s4); + NameList.visit(namelist1, new NameList.Visitor() { + public void visit(String s5) throws IOException { + job.readErrorExamples(s5); + } + }); + if (simplehandler.getNumFailures() > 0) { + return; + } + if (flag) { + (new ParserOutput(simplehandler, job)).write(dir + settings.getClassName() + ".java"); + } + if (flag1) { + (new TokensOutput(simplehandler, job)).write(dir + settings.getInterfaceName() + ".java"); + } + if (flag2) { + (new TextOutput(simplehandler, job, flag4)).write(dir + s4 + ".output"); + } + final boolean showState = flag5; + NameList.visit(namelist2, new NameList.Visitor() { + public void visit(String s5) throws IOException { + job.readRunExample(s5, showState); + } + }); + } + + private static void usage(String s) { + System.err.println(s); + System.err.println("usage: jacc [options] file.jacc ..."); + System.err.println("options (individually, or in combination):"); + System.err.println(" -p do not generate parser"); + System.err.println(" -t do not generate token specification"); + System.err.println(" -v output text description of machine"); + System.err.println(" -f show first/follow sets (with -h or -v)"); + System.err.println(" -a treat as LALR(1) grammar (default)"); + System.err.println(" -s treat as SLR(1) grammar"); + System.err.println(" -0 treat as LR(0) grammar"); + System.err.println(" -r file run parser on input in file"); + System.err.println(" -n show state numbers in parser output"); + System.err.println(" -e file read error cases from file"); + System.err.println(" -d dir output files to directory"); + System.exit(1); + } + + private static class NameList { + + String name; + NameList names; + + NameList(String s, NameList namelist) { + name = s; + names = namelist; + } + + static void visit(NameList namelist, Visitor visitor) throws IOException { + if (namelist != null) { + visit(namelist.names, visitor); + visitor.visit(namelist.name); + } + } + + String getFirst() { + NameList namelist; + namelist = this; + while (namelist.names != null) { + namelist = namelist.names; + } + return namelist.name; + } + + interface Visitor { + void visit(String s) throws IOException; + } + } +} diff --git a/src/main/java/org/xbib/jacc/JaccJob.java b/src/main/java/org/xbib/jacc/JaccJob.java new file mode 100644 index 0000000..3656c54 --- /dev/null +++ b/src/main/java/org/xbib/jacc/JaccJob.java @@ -0,0 +1,182 @@ +package org.xbib.jacc; + +import org.xbib.jacc.compiler.Failure; +import org.xbib.jacc.compiler.Handler; +import org.xbib.jacc.compiler.JavaSource; +import org.xbib.jacc.compiler.Phase; +import org.xbib.jacc.compiler.Position; +import org.xbib.jacc.compiler.Warning; +import org.xbib.jacc.grammar.Finitary; +import org.xbib.jacc.grammar.Grammar; +import org.xbib.jacc.grammar.LookaheadMachine; +import org.xbib.jacc.grammar.Parser; + +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.Reader; +import java.io.Writer; +import java.nio.charset.StandardCharsets; + +class JaccJob extends Phase { + + private Settings settings; + + private JaccParser parser; + + private JaccTables tables; + + private JaccResolver resolver; + + private Writer out; + + JaccJob(Handler handler, Writer out, Settings settings) { + super(handler); + this.out = out; + this.settings = settings; + this.parser = new JaccParser(handler, settings); + } + + Settings getSettings() { + return settings; + } + + JaccTables getTables() { + return tables; + } + + JaccResolver getResolver() { + return resolver; + } + + private JaccLexer lexerFromFile(String s) { + JaccLexer jacclexer; + try { + Reader filereader = new InputStreamReader(new FileInputStream(s), StandardCharsets.UTF_8); + jacclexer = new JaccLexer(getHandler(), new JavaSource(getHandler(), s, filereader)); + jacclexer.nextToken(); + return jacclexer; + } catch (IOException e) { + report(new Failure("Could not open file \"" + s + "\"")); + return null; + } + } + + void parseGrammarFile(String s) throws IOException { + JaccLexer jacclexer = lexerFromFile(s); + if (jacclexer != null) { + parser.parse(jacclexer); + } + } + + void buildTables() { + Grammar grammar = parser.getGrammar(); + if (grammar == null || !allDeriveFinite(grammar)) { + return; + } + LookaheadMachine lookaheadmachine = settings.makeMachine(grammar); + resolver = new JaccResolver(lookaheadmachine); + tables = new JaccTables(lookaheadmachine, resolver); + if (tables.getProdUnused() > 0) { + report(new Warning(tables.getProdUnused() + " rules never reduced")); + } + if (resolver.getNumSRConflicts() > 0 || resolver.getNumRRConflicts() > 0) { + report(new Warning("conflicts: " + + resolver.getNumSRConflicts() + + " shift/reduce, " + + resolver.getNumRRConflicts() + + " reduce/reduce")); + } + } + + private boolean allDeriveFinite(Grammar grammar) { + Finitary finitary = grammar.getFinitary(); + boolean flag = true; + for (int i = 0; i < grammar.getNumNTs(); i++) { + if (!finitary.at(i)) { + flag = false; + report(new Failure("No finite strings can be derived for " + + grammar.getNonterminal(i))); + } + } + return flag; + } + + void readRunExample(String s, boolean flag) throws IOException { + out.write("Running example from \"" + s + "\"]\n"); + JaccLexer jacclexer = lexerFromFile(s); + if (jacclexer != null) { + runExample(parser.parseSymbols(jacclexer), flag); + } + } + + private void runExample(int ai[], boolean flag) throws IOException { + Grammar grammar = parser.getGrammar(); + Parser parser1 = new Parser(tables, ai); + out.write("start "); + do { + out.write(" : "); + parser1.display(out, flag); + switch (parser1.step()) { + case 0: + out.write("Accept!\n"); + return; + case 1: + out.write("error in state "); + out.write(parser1.getState()); + out.write(", next symbol "); + out.write(grammar.getSymbol(parser1.getNextSymbol()).toString()); + return; + case 3: + out.write("goto "); + break; + case 2: + out.write("shift "); + break; + case 4: + out.write("reduce"); + break; + default: + break; + } + } while (true); + } + + void readErrorExamples(String s) throws IOException { + out.write("Reading error examples from \"" + s + "\""); + JaccLexer jacclexer = lexerFromFile(s); + if (jacclexer != null) { + parser.parseErrorExamples(jacclexer, this); + } + } + + void errorExample(Position position, String s, int ai[]) { + Parser parser1 = new Parser(tables, ai); + int i; + do { + i = parser1.step(); + } while (i != 0 && i != 1); + if (i == 0) { + report(new Warning(position, "Example for \"" + s + "\" does not produce an error")); + } else { + Grammar grammar = tables.getMachine().getGrammar(); + int j = parser1.getNextSymbol(); + if (grammar.isNonterminal(j)) { + report(new Warning(position, + "Example for \"" + s + "\" reaches an error at the nonterminal " + grammar.getSymbol(j))); + } else { + int k = parser1.getState(); + if (!tables.errorAt(k, j)) { + report(new Failure(position, "Error example results in internal error")); + } else { + String s1 = tables.errorSet(k, j, s); + if (s1 != null) { + report(new Warning(position, + "Multiple errors are possible in state " + k + " on terminal " + + grammar.getSymbol(j) + ":\n - " + s1 + "\n - " + s)); + } + } + } + } + } +} diff --git a/src/main/java/org/xbib/jacc/JaccLexer.java b/src/main/java/org/xbib/jacc/JaccLexer.java new file mode 100644 index 0000000..70f0553 --- /dev/null +++ b/src/main/java/org/xbib/jacc/JaccLexer.java @@ -0,0 +1,419 @@ +package org.xbib.jacc; + +import org.xbib.jacc.compiler.Failure; +import org.xbib.jacc.compiler.Handler; +import org.xbib.jacc.compiler.Source; +import org.xbib.jacc.compiler.SourceLexer; +import org.xbib.jacc.compiler.Warning; + +import java.io.IOException; + +/** + * + */ +class JaccLexer extends SourceLexer implements JaccTokens { + + private int lastLiteral; + + JaccLexer(Handler handler, Source source) throws IOException { + super(handler, source); + } + + @Override + public int nextToken() throws IOException { + do { + skipWhitespace(); + markPosition(); + lexemeText = null; + switch (c) { + case -1: + return token = ENDINPUT; + + case 58: // ':' + nextChar(); + return token = COLON; + + case 59: // ';' + nextChar(); + return token = SEMI; + + case 124: // '|' + nextChar(); + return token = BAR; + + case 60: // '<' + nextChar(); + return token = TOPEN; + + case 62: // '>' + nextChar(); + return token = TCLOSE; + + case 91: // '[' + nextChar(); + return token = BOPEN; + + case 93: // ']' + nextChar(); + return token = BCLOSE; + + case 46: // '.' + nextChar(); + return token = DOT; + + case 37: // '%' + if (directive() != -1) { + return token; + } + break; + + case 34: // '"' + if (string() != -1) { + return token; + } + break; + + case 39: // '\'' + if (literal() != -1) { + return token; + } + break; + + case 123: // '{' + if (action() != -1) { + return token; + } + break; + + case 47: // '/' + skipComment(); + break; + + default: + if (Character.isJavaIdentifierStart((char) c)) { + return identifier(); + } + if (Character.isDigit((char) c)) { + return number(); + } + illegalCharacter(); + nextChar(); + break; + } + } while (true); + } + + String readWholeLine() throws IOException { + if (line == null) { + return null; + } + String s = line; + if (col > 0) { + s = s.substring(col); + } + nextLine(); + return s; + } + + String readCodeLine() throws IOException { + while (isWhitespace(c)) { + nextChar(); + } + return readWholeLine(); + } + + private boolean isWhitespace(int i) { + return i == 32 || i == 12; + } + + private void skipWhitespace() throws IOException { + while (isWhitespace(c)) { + nextChar(); + } + while (c == 10) { + nextLine(); + while (isWhitespace(c)) { + nextChar(); + } + } + } + + private void skipComment() throws IOException { + nextChar(); + if (c == 47) { + nextLine(); + } else { + if (c == 42) { + nextChar(); + do { + if (c == 42) { + do { + nextChar(); + } while (c == 42); + if (c == 47) { + nextChar(); + return; + } + } + if (c == -1) { + report(new Failure(getPos(), "Unterminated comment")); + return; + } + if (c == 10) { + nextLine(); + } else { + nextChar(); + } + } while (true); + } + report(new Failure(getPos(), "Illegal comment format")); + } + } + + private int identifier() { + int i = col; + do { + nextChar(); + } while (c != -1 && Character.isJavaIdentifierPart((char) c)); + lexemeText = line.substring(i, col); + return token = IDENT; + } + + private int directive() throws IOException { + nextChar(); + if (c == 37) { + nextChar(); + return token = MARK; + } + if (Character.isJavaIdentifierStart((char) c)) { + identifier(); + if (lexemeText.equals("token")) { + return token = TOKEN; + } + if (lexemeText.equals("type")) { + return token = TYPE; + } + if (lexemeText.equals("prec")) { + return token = PREC; + } + if (lexemeText.equals("left")) { + return token = LEFT; + } + if (lexemeText.equals("right")) { + return token = RIGHT; + } + if (lexemeText.equals("nonassoc")) { + return token = NONASSOC; + } + if (lexemeText.equals("start")) { + return token = START; + } + if (lexemeText.equals("package")) { + return token = PACKAGE; + } + if (lexemeText.equals("extends")) { + return token = EXTENDS; + } + if (lexemeText.equals("implements")) { + return token = IMPLEMENTS; + } + if (lexemeText.equals("semantic")) { + return token = SEMANTIC; + } + if (lexemeText.equals("get")) { + return token = GETTOKEN; + } + if (lexemeText.equals("next")) { + return token = NEXTTOKEN; + } + if (lexemeText.equals("class")) { + return token = CLASS; + } + if (lexemeText.equals("interface")) { + return token = INTERFACE; + } else { + report(new Failure(getPos(), "Unrecognized directive")); + return ERROR; + } + } + if (c == 123) { + nextChar(); + return code(); + } else { + report(new Failure(getPos(), "Illegal directive syntax")); + return ERROR; + } + } + + private int code() throws IOException { + int i = col; + StringBuilder sb = null; + do { + if (c == 37) { + do { + nextChar(); + } while (c == 37); + if (c == 125) { + lexemeText = endBuffer(sb, i, col - 1); + nextChar(); + return token = CODE; + } + } + if (c == -1) { + report(new Failure(getPos(), "Code fragment terminator %} not found")); + lexemeText = endBuffer(sb, i, col); + return token = CODE; + } + if (c == 10) { + if (sb == null) { + sb = new StringBuilder(line.substring(i, col)); + } else { + sb.append('\n'); + sb.append(line); + } + nextLine(); + } else { + nextChar(); + } + } while (true); + } + + private String endBuffer(StringBuilder sb, int i, int j) { + if (sb == null) { + return line.substring(i, j); + } + sb.append('\n'); + if (line != null) { + sb.append(line.substring(0, j)); + } + return sb.toString(); + } + + int getLastLiteral() { + return lastLiteral; + } + + private int number() { + int i = col; + int j = 0; + int k = Character.digit((char) c, 10); + do { + j = 10 * j + k; + nextChar(); + k = Character.digit((char) c, 10); + } while (k >= 0); + lexemeText = line.substring(i, col); + lastLiteral = j; + return token = INTLIT; + } + + private int string() { + nextChar(); + int i = col; + while (c != 34 && c != 10 && c != -1) { + if (c == 92) { + escapeChar(); + } else { + nextChar(); + } + } + lexemeText = line.substring(i, col); + if (c == 34) { + nextChar(); + } else { + report(new Warning(getPos(), "Missing \" on string literal")); + } + return token = STRLIT; + } + + private int literal() { + int i = col; + nextChar(); + if (c == 92) { + escapeChar(); + } else { + if (c != 39 && c != 10 && c != -1) { + lastLiteral = c; + nextChar(); + } else { + report(new Failure(getPos(), "Illegal character literal syntax")); + return ERROR; + } + } + if (c == 39) { + nextChar(); + } else { + report(new Warning(getPos(), "Missing ' on character literal")); + } + lexemeText = line.substring(i, col); + return token = CHARLIT; + } + + private void escapeChar() { + nextChar(); + switch (c) { + case 34: // '"' + case 39: // '\'' + case 92: // '\\' + case 98: // 'b' + case 102: // 'f' + case 110: // 'n' + case 114: // 'r' + case 116: // 't' + lastLiteral = c; + nextChar(); + return; + } + int i = Character.digit((char) c, 8); + if (i >= 0) { + lastLiteral = 0; + int j = i >= 4 ? 2 : 3; + do { + lastLiteral = (lastLiteral << 3) + i; + nextChar(); + i = Character.digit((char) c, 8); + } while (i >= 0 && --j > 0); + } else { + report(new Failure(getPos(), "Syntax error in escape sequence")); + } + } + + private int action() throws IOException { + int i = col; + int j = 0; + StringBuilder sb = null; + do { + if (c == 125) { + if (--j == 0) { + nextChar(); + lexemeText = endBuffer(sb, i, col); + return token = ACTION; + } + } else { + if (c == 123) { + j++; + } + } + if (c == -1) { + report(new Failure(getPos(), "Unterminated action")); + lexemeText = endBuffer(sb, i, col); + return token = ACTION; + } + if (c == 10) { + if (sb == null) { + sb = new StringBuilder(line.substring(i, col)); + } else { + sb.append('\n'); + sb.append(line); + } + nextLine(); + } else { + nextChar(); + } + } while (true); + } + + private void illegalCharacter() { + report(new Warning(getPos(), "Ignoring illegal character")); + } +} diff --git a/src/main/java/org/xbib/jacc/JaccParser.java b/src/main/java/org/xbib/jacc/JaccParser.java new file mode 100644 index 0000000..06e05ad --- /dev/null +++ b/src/main/java/org/xbib/jacc/JaccParser.java @@ -0,0 +1,629 @@ +package org.xbib.jacc; + +import org.xbib.jacc.compiler.Failure; +import org.xbib.jacc.compiler.Handler; +import org.xbib.jacc.compiler.Phase; +import org.xbib.jacc.compiler.Position; +import org.xbib.jacc.compiler.Warning; +import org.xbib.jacc.grammar.Grammar; + +import java.io.IOException; + +/** + * + */ +class JaccParser extends Phase implements JaccTokens { + + private Settings settings; + private int seqNo; + private JaccLexer lexer; + private int precedence; + private NamedJaccSymbols terminals; + private NamedJaccSymbols nonterms; + private NumJaccSymbols literals; + private JaccSymbol start; + JaccParser(Handler handler, Settings settings1) { + super(handler); + seqNo = 1; + precedence = 0; + settings = settings1; + terminals = new NamedJaccSymbols(); + nonterms = new NamedJaccSymbols(); + literals = new NumJaccSymbols(); + start = null; + } + + public Grammar getGrammar() { + try { + JaccSymbol ajaccsymbol[]; + JaccProd ajaccprod[][]; + int i = nonterms.getSize(); + int j = terminals.getSize() + literals.getSize() + 1; + if (i == 0 || start == null) { + report(new Failure("No nonterminals defined")); + return null; + } + ajaccsymbol = new JaccSymbol[i + j]; + literals.fill(ajaccsymbol, terminals.fill(ajaccsymbol, nonterms.fill(ajaccsymbol, 0))); + ajaccsymbol[(i + j) - 1] = new JaccSymbol("$end"); + ajaccsymbol[(i + j) - 1].setNum(0); + int k = 1; + for (int l = 0; l < j - 1; l++) { + if (ajaccsymbol[i + l].getNum() >= 0) { + continue; + } + while (literals.find(k) != null) { + k++; + } + ajaccsymbol[i + l].setNum(k++); + } + int i1 = 0; + do { + if (i1 >= i) { + break; + } + if (ajaccsymbol[i1] == start) { + if (i1 > 0) { + JaccSymbol jaccsymbol = ajaccsymbol[0]; + ajaccsymbol[0] = ajaccsymbol[i1]; + ajaccsymbol[i1] = jaccsymbol; + } + break; + } + i1++; + } while (true); + for (int j1 = 0; j1 < ajaccsymbol.length; j1++) { + ajaccsymbol[j1].setTokenNo(j1); + } + ajaccprod = new JaccProd[nonterms.getSize()][]; + for (int k1 = 0; k1 < ajaccprod.length; k1++) { + ajaccprod[k1] = ajaccsymbol[k1].getProds(); + if (ajaccprod[k1] == null || ajaccprod[k1].length == 0) { + report(new Failure("No productions for " + ajaccsymbol[k1].getName())); + } + } + return new Grammar(ajaccsymbol, ajaccprod); + } catch (Exception e) { + report(new Failure("Internal problem " + e.getMessage())); + return null; + } + } + + void parse(JaccLexer jacclexer) throws IOException { + lexer = jacclexer; + terminals.findOrAdd("error"); + parseDefinitions(); + if (jacclexer.getToken() != 1) { + report(new Failure(jacclexer.getPos(), "Missing grammar")); + } else { + jacclexer.nextToken(); + parseGrammar(); + String s; + if (jacclexer.getToken() == 1) { + while ((s = jacclexer.readWholeLine()) != null) { + settings.addPostText(s); + settings.addPostText("\n"); + } + } + } + jacclexer.close(); + } + + int[] parseSymbols(JaccLexer jacclexer) throws IOException { + lexer = jacclexer; + SymList symlist = null; + do { + JaccSymbol jaccsymbol = parseDefinedSymbol(); + if (jaccsymbol == null) { + if (jacclexer.getToken() != 0) { + report(new Warning(jacclexer.getPos(), "Ignoring extra tokens at end of input")); + } + jacclexer.close(); + return SymList.toIntArray(symlist); + } + symlist = new SymList(jaccsymbol, symlist); + jacclexer.nextToken(); + } while (true); + } + + void parseErrorExamples(JaccLexer jacclexer, JaccJob jaccjob) throws IOException { + lexer = jacclexer; + do { + if (jacclexer.getToken() != 5) { + break; + } + String s = jacclexer.getLexeme(); + if (jacclexer.nextToken() == 58) { + jacclexer.nextToken(); + } else { + report(new Warning(jacclexer.getPos(), "A colon was expected here")); + } + int i; + do { + Position position = jacclexer.getPos(); + SymList symlist = null; + JaccSymbol jaccsymbol; + while ((jaccsymbol = parseDefinedSymbol()) != null) { + symlist = new SymList(jaccsymbol, symlist); + jacclexer.nextToken(); + } + int ai[] = SymList.toIntArray(symlist); + jaccjob.errorExample(position, s, ai); + i = jacclexer.getToken(); + if (i != 124) { + break; + } + jacclexer.nextToken(); + } while (true); + if (i != 0) { + if (i != 59) { + report(new Failure(jacclexer.getPos(), "Unexpected token; a semicolon was expected here")); + do { + i = jacclexer.nextToken(); + } while (i != 59 && i != 0); + } + if (i == 59) { + jacclexer.nextToken(); + } + } + } while (true); + if (jacclexer.getToken() != 0) { + report(new Failure(jacclexer.getPos(), "Unexpected token; ignoring the rest of this file")); + } + jacclexer.close(); + } + + private void parseDefinitions() throws IOException { + boolean flag = false; + do { + switch (lexer.getToken()) { + case 0: // '\0' + case 1: // '\001' + return; + } + if (parseDefinition()) { + flag = false; + } else { + if (!flag) { + flag = true; + report(new Failure(lexer.getPos(), "Syntax error in definition")); + } + lexer.nextToken(); + } + } while (true); + } + + private boolean parseDefinition() throws IOException { + switch (lexer.getToken()) { + case 2: // '\002' + settings.addPreText(lexer.getLexeme()); + lexer.nextToken(); + return true; + + case 8: // '\b' + parseTokenDefn(); + return true; + + case 9: // '\t' + parseTypeDefn(); + return true; + + case 11: // '\013' + parseFixityDefn(Fixity.left(precedence++)); + return true; + + case 13: // '\r' + parseFixityDefn(Fixity.nonass(precedence++)); + return true; + + case 12: // '\f' + parseFixityDefn(Fixity.right(precedence++)); + return true; + + case 14: // '\016' + parseStart(); + return true; + + case 16: // '\020' + settings.setClassName(parseIdent(lexer.getLexeme(), settings.getClassName())); + return true; + + case 17: // '\021' + settings.setInterfaceName(parseIdent(lexer.getLexeme(), settings.getInterfaceName())); + return true; + + case 15: // '\017' + settings.setPackageName(parseDefnQualName(lexer.getLexeme(), settings.getPackageName())); + return true; + + case 18: // '\022' + settings.setExtendsName(parseDefnQualName(lexer.getLexeme(), settings.getExtendsName())); + return true; + + case 19: // '\023' + lexer.nextToken(); + String s = parseQualName(); + if (s != null) { + settings.addImplementsNames(s); + } + return true; + + case 20: // '\024' + settings.setTypeName(parseDefnQualName(lexer.getLexeme(), settings.getTypeName())); + if (lexer.getToken() == 58) { + settings.setGetSemantic(lexer.readCodeLine()); + lexer.nextToken(); + } + return true; + + case 21: // '\025' + settings.setGetToken(lexer.readCodeLine()); + lexer.nextToken(); + return true; + + case 22: // '\026' + settings.setNextToken(lexer.readCodeLine()); + lexer.nextToken(); + return true; + + case 3: // '\003' + case 4: // '\004' + case 5: // '\005' + case 6: // '\006' + case 7: // '\007' + case 10: // '\n' + default: + return false; + } + } + + private void parseStart() throws IOException { + Position position = lexer.getPos(); + lexer.nextToken(); + JaccSymbol jaccsymbol = parseNonterminal(); + if (jaccsymbol == null) { + report(new Failure(position, "Missing start symbol")); + } else { + if (start == null) { + start = jaccsymbol; + } else { + report(new Failure(position, "Multiple %start definitions are not permitted")); + } + lexer.nextToken(); + } + } + + private String parseIdent(String s, String s1) throws IOException { + Position position = lexer.getPos(); + if (lexer.nextToken() != 3) { + report(new Failure(lexer.getPos(), "Syntax error in %" + s + " directive; identifier expected")); + return s1; + } + String s2 = lexer.getLexeme(); + lexer.nextToken(); + if (s2 != null && s1 != null) { + report(new Failure(position, "Multiple %" + s + " definitions are not permitted")); + s2 = s1; + } + return s2; + } + + private String parseDefnQualName(String s, String s1) throws IOException { + Position position = lexer.getPos(); + lexer.nextToken(); + String s2 = parseQualName(); + if (s2 != null && s1 != null) { + report(new Failure(position, "Multiple %" + s + " definitions are not permitted")); + s2 = s1; + } + return s2; + } + + private void parseTokenDefn() throws IOException { + Position position = lexer.getPos(); + String s = optionalType(); + int i = 0; + do { + JaccSymbol jaccsymbol = parseTerminal(); + if (jaccsymbol == null) { + if (i == 0) { + report(new Failure(position, "Missing symbols in %token definition")); + } + return; + } + addType(jaccsymbol, s); + lexer.nextToken(); + i++; + } while (true); + } + + private void parseTypeDefn() throws IOException { + Position position = lexer.getPos(); + String s = optionalType(); + int i = 0; + do { + JaccSymbol jaccsymbol = parseSymbol(); + if (jaccsymbol == null) { + if (i == 0) { + report(new Failure(position, "Missing symbols in %type definition")); + } + return; + } + addType(jaccsymbol, s); + lexer.nextToken(); + i++; + } while (true); + } + + private void parseFixityDefn(Fixity fixity) throws IOException { + Position position = lexer.getPos(); + String s = optionalType(); + int i = 0; + do { + JaccSymbol jaccsymbol = parseTerminal(); + if (jaccsymbol == null) { + if (i == 0) { + report(new Failure(position, "Missing symbols in fixity definition")); + } + return; + } + addFixity(jaccsymbol, fixity); + addType(jaccsymbol, s); + lexer.nextToken(); + i++; + } while (true); + } + + private String optionalType() throws IOException { + label0: + { + StringBuilder sb = new StringBuilder(); + label1: + { + if (lexer.nextToken() != 60) { + break label0; + } + lexer.nextToken(); + sb.append(parseQualName()); + do { + if (lexer.getToken() != 91) { + break label1; + } + if (lexer.nextToken() != 93) { + break; + } + lexer.nextToken(); + sb.append("[]"); + } while (true); + report(new Failure(lexer.getPos(), "Missing ']' in array type")); + } + if (lexer.getToken() == 62) { + lexer.nextToken(); + } else if (sb.length() > 0) { + report(new Failure(lexer.getPos(), "Missing `>' in type specification")); + } + return sb.toString(); + } + return null; + } + + private void addFixity(JaccSymbol jaccsymbol, Fixity fixity) { + if (!jaccsymbol.setFixity(fixity)) { + report(new Warning(lexer.getPos(), "Cannot change fixity for " + jaccsymbol.getName())); + } + } + + private void addType(JaccSymbol jaccsymbol, String s) { + if (s != null && !jaccsymbol.setType(s)) { + report(new Warning(lexer.getPos(), "Cannot change type for " + jaccsymbol.getName())); + } + } + + private void parseGrammar() throws IOException { + JaccSymbol jaccsymbol; + while ((jaccsymbol = parseLhs()) != null) { + if (start == null) { + start = jaccsymbol; + } + jaccsymbol.addProduction(parseRhs()); + for (; lexer.getToken() == 124; jaccsymbol.addProduction(parseRhs())) { + lexer.nextToken(); + } + + if (lexer.getToken() == 59) { + lexer.nextToken(); + } else { + report(new Warning(lexer.getPos(), "Missing ';' at end of rule")); + } + } + } + + private JaccSymbol parseLhs() throws IOException { + boolean flag = false; + for (int i = lexer.getToken(); i != 1 && i != 0; ) { + JaccSymbol jaccsymbol = parseNonterminal(); + if (jaccsymbol == null) { + if (!flag) { + if (parseTerminal() != null) { + report(new Failure(lexer.getPos(), "Terminal symbol used on left hand side of rule")); + } else { + report(new Failure(lexer.getPos(), "Missing left hand side in rule")); + } + flag = true; + } + i = lexer.nextToken(); + } else { + i = lexer.nextToken(); + if (i != 58) { + if (!flag) { + report(new Failure(lexer.getPos(), "Missing colon after left hand side of rule")); + } + flag = true; + } else { + lexer.nextToken(); + return jaccsymbol; + } + } + } + + return null; + } + + private JaccProd parseRhs() throws IOException { + Fixity fixity = null; + SymList symlist = null; + do { + while (lexer.getToken() == 10) { + lexer.nextToken(); + JaccSymbol jaccsymbol = parseSymbol(); + if (jaccsymbol == null) { + report(new Failure(lexer.getPos(), "Missing token for %prec directive")); + } else if (jaccsymbol.getFixity() == null) { + report(new Failure(lexer.getPos(), + "Ignoring %prec annotation because no fixity has been specified for " + + jaccsymbol.getName())); + lexer.nextToken(); + } else { + if (fixity != null) { + report(new Warning(lexer.getPos(), "Multiple %prec annotations in production")); + } + fixity = jaccsymbol.getFixity(); + lexer.nextToken(); + } + } + JaccSymbol jaccsymbol1 = parseSymbol(); + if (jaccsymbol1 == null) { + break; + } + symlist = new SymList(jaccsymbol1, symlist); + lexer.nextToken(); + } while (true); + String s = null; + Position position = null; + if (lexer.getToken() == 7) { + s = lexer.getLexeme(); + position = lexer.getPos(); + lexer.nextToken(); + } + JaccSymbol ajaccsymbol[] = SymList.toArray(symlist); + return new JaccProd(fixity, ajaccsymbol, position, s, seqNo++); + } + + private String parseQualName() throws IOException { + if (lexer.getToken() != 3) { + report(new Failure(lexer.getPos(), "Syntax error in qualified name; identifier expected")); + return null; + } + StringBuilder stringbuffer = new StringBuilder(); + do { + stringbuffer.append(lexer.getLexeme()); + if (lexer.nextToken() != 46) { + break; + } + if (lexer.nextToken() != 3) { + report(new Failure(lexer.getPos(), "Syntax error in qualified name")); + break; + } + stringbuffer.append('.'); + } while (true); + return stringbuffer.toString(); + } + + private JaccSymbol parseTerminal() { + String s = lexer.getLexeme(); + switch (lexer.getToken()) { + case 3: // '\003' + if (nonterms.find(s) != null) { + return null; + } else { + return terminals.findOrAdd(s); + } + + case 4: // '\004' + return literals.findOrAdd(s, lexer.getLastLiteral()); + } + return null; + } + + private JaccSymbol parseNonterminal() { + String s = lexer.getLexeme(); + switch (lexer.getToken()) { + case 3: // '\003' + if (terminals.find(s) != null) { + return null; + } else { + return nonterms.findOrAdd(s); + } + } + return null; + } + + private JaccSymbol parseSymbol() { + String s = lexer.getLexeme(); + switch (lexer.getToken()) { + case 3: // '\003' + JaccSymbol jaccsymbol; + jaccsymbol = terminals.find(s); + if (jaccsymbol == null) { + jaccsymbol = nonterms.findOrAdd(s); + } + return jaccsymbol; + + case 4: // '\004' + return literals.findOrAdd(s, lexer.getLastLiteral()); + } + return null; + } + + private JaccSymbol parseDefinedSymbol() { + String s = lexer.getLexeme(); + switch (lexer.getToken()) { + case 3: // '\003' + JaccSymbol jaccsymbol = nonterms.find(s); + return jaccsymbol == null ? terminals.find(s) : jaccsymbol; + + case 4: // '\004' + return literals.find(lexer.getLastLiteral()); + } + return null; + } + + private static class SymList { + + JaccSymbol head; + SymList tail; + + SymList(JaccSymbol jaccsymbol, SymList symlist) { + head = jaccsymbol; + tail = symlist; + } + + static int length(SymList symlist) { + int i = 0; + for (; symlist != null; symlist = symlist.tail) { + i++; + } + return i; + } + + static JaccSymbol[] toArray(SymList symlist) { + int i = length(symlist); + JaccSymbol ajaccsymbol[] = new JaccSymbol[i]; + while (i > 0) { + ajaccsymbol[--i] = symlist.head; + symlist = symlist.tail; + } + return ajaccsymbol; + } + + static int[] toIntArray(SymList symlist) { + int i = length(symlist); + int ai[] = new int[i]; + while (i > 0) { + ai[--i] = symlist.head.getTokenNo(); + symlist = symlist.tail; + } + return ai; + } + } +} diff --git a/src/main/java/org/xbib/jacc/JaccProd.java b/src/main/java/org/xbib/jacc/JaccProd.java new file mode 100644 index 0000000..2cb4c36 --- /dev/null +++ b/src/main/java/org/xbib/jacc/JaccProd.java @@ -0,0 +1,54 @@ +package org.xbib.jacc; + +import org.xbib.jacc.compiler.Position; +import org.xbib.jacc.grammar.Grammar; + +/** + * + */ +class JaccProd extends Grammar.Prod { + + private Fixity fixity; + private JaccSymbol[] prodSyms; + private Position actPos; + private String action; + + JaccProd(Fixity fixity, JaccSymbol[] jaccsymbol, Position position, String s, int i) { + super(new int[jaccsymbol.length], i); + this.fixity = fixity; + prodSyms = jaccsymbol; + actPos = position; + action = s; + } + + public String getLabel() { + return Integer.toString(getSeqNo()); + } + + void fixup() { + int[] ai = getRhs(); + for (int i = 0; i < prodSyms.length; i++) { + ai[i] = prodSyms[i].getTokenNo(); + } + } + + Fixity getFixity() { + if (fixity == null) { + for (int i = prodSyms.length - 1; i >= 0; i--) { + Fixity fixity1 = prodSyms[i].getFixity(); + if (fixity1 != null) { + return fixity1; + } + } + } + return fixity; + } + + Position getActionPos() { + return actPos; + } + + String getAction() { + return action; + } +} diff --git a/src/main/java/org/xbib/jacc/JaccResolver.java b/src/main/java/org/xbib/jacc/JaccResolver.java new file mode 100644 index 0000000..19c8c43 --- /dev/null +++ b/src/main/java/org/xbib/jacc/JaccResolver.java @@ -0,0 +1,73 @@ +package org.xbib.jacc; + +import org.xbib.jacc.grammar.Grammar; +import org.xbib.jacc.grammar.LR0Items; +import org.xbib.jacc.grammar.LookaheadMachine; +import org.xbib.jacc.grammar.Resolver; +import org.xbib.jacc.grammar.Tables; +import org.xbib.jacc.util.IntSet; + +class JaccResolver implements Resolver { + + private LookaheadMachine machine; + private int numSRConflicts; + private int numRRConflicts; + private Conflicts conflicts[]; + + JaccResolver(LookaheadMachine lookaheadmachine) { + numSRConflicts = 0; + numRRConflicts = 0; + machine = lookaheadmachine; + conflicts = new Conflicts[lookaheadmachine.getNumStates()]; + } + + int getNumSRConflicts() { + return numSRConflicts; + } + + int getNumRRConflicts() { + return numRRConflicts; + } + + String getConflictsAt(int i) { + return Conflicts.describe(machine, i, conflicts[i]); + } + + @Override + public void srResolve(Tables tables, int i, int j, int k) { + Grammar grammar = machine.getGrammar(); + Grammar.Symbol symbol = grammar.getTerminal(j); + IntSet intset = machine.getItemsAt(i); + LR0Items lr0items = machine.getItems(); + Grammar.Prod prod = lr0items.getItem(intset.at(k)).getProd(); + if ((symbol instanceof JaccSymbol) && (prod instanceof JaccProd)) { + JaccSymbol jaccsymbol = (JaccSymbol) symbol; + JaccProd jaccprod = (JaccProd) prod; + switch (Fixity.which(jaccprod.getFixity(), jaccsymbol.getFixity())) { + case 1: + tables.setReduce(i, j, k); + return; + case 3: + return; + } + } + conflicts[i] = Conflicts.sr(tables.getArgAt(i)[j], k, symbol, conflicts[i]); + numSRConflicts++; + } + + @Override + public void rrResolve(Tables tables, int i, int j, int k) { + Grammar grammar = machine.getGrammar(); + int l = tables.getArgAt(i)[j]; + IntSet intset = machine.getItemsAt(i); + LR0Items lr0items = machine.getItems(); + Grammar.Prod prod = lr0items.getItem(intset.at(l)).getProd(); + Grammar.Prod prod1 = lr0items.getItem(intset.at(k)).getProd(); + Grammar.Symbol symbol = grammar.getTerminal(j); + if (prod1.getSeqNo() < prod.getSeqNo()) { + tables.setReduce(i, j, k); + } + conflicts[i] = Conflicts.rr(l, k, symbol, conflicts[i]); + numRRConflicts++; + } +} diff --git a/src/main/java/org/xbib/jacc/JaccSymbol.java b/src/main/java/org/xbib/jacc/JaccSymbol.java new file mode 100644 index 0000000..6f24658 --- /dev/null +++ b/src/main/java/org/xbib/jacc/JaccSymbol.java @@ -0,0 +1,97 @@ +package org.xbib.jacc; + +import org.xbib.jacc.grammar.Grammar; + +/** + * + */ +class JaccSymbol extends Grammar.Symbol { + + private int num; + private int tokenNo; + private JaccProd[] jaccProds; + private int pused; + private Fixity fixity; + private String type; + + JaccSymbol(String s, int i) { + super(s); + tokenNo = -1; + jaccProds = null; + pused = 0; + num = i; + } + + JaccSymbol(String s) { + this(s, -1); + } + + int getNum() { + return num; + } + + void setNum(int i) { + if (num < 0) { + num = i; + } + } + + int getTokenNo() { + return tokenNo; + } + + void setTokenNo(int i) { + if (tokenNo < 0) { + tokenNo = i; + } + } + + void addProduction(JaccProd jaccprod) { + if (jaccProds == null) { + jaccProds = new JaccProd[1]; + } else { + if (pused >= jaccProds.length) { + JaccProd ajaccprod[] = new JaccProd[2 * jaccProds.length]; + System.arraycopy(jaccProds, 0, ajaccprod, 0, jaccProds.length); + + jaccProds = ajaccprod; + } + } + jaccProds[pused++] = jaccprod; + } + + public JaccProd[] getProds() { + JaccProd ajaccprod[] = new JaccProd[pused]; + for (int i = 0; i < pused; i++) { + ajaccprod[i] = jaccProds[i]; + ajaccprod[i].fixup(); + } + return ajaccprod; + } + + boolean setFixity(Fixity fixity1) { + if (fixity == null) { + fixity = fixity1; + return true; + } else { + return fixity1.equalsFixity(fixity); + } + } + + Fixity getFixity() { + return fixity; + } + + boolean setType(String s) { + if (type == null) { + type = s; + return true; + } else { + return s.compareTo(type) == 0; + } + } + + String getType() { + return type; + } +} diff --git a/src/main/java/org/xbib/jacc/JaccSymbols.java b/src/main/java/org/xbib/jacc/JaccSymbols.java new file mode 100644 index 0000000..15af15c --- /dev/null +++ b/src/main/java/org/xbib/jacc/JaccSymbols.java @@ -0,0 +1,38 @@ +package org.xbib.jacc; + +abstract class JaccSymbols { + Node root; + int size; + JaccSymbols() { + root = null; + size = 0; + } + + private static int fill(JaccSymbol ajaccsymbol[], int i, Node node) { + if (node != null) { + i = fill(ajaccsymbol, i, node.left); + ajaccsymbol[i++] = node.data; + i = fill(ajaccsymbol, i, node.right); + } + return i; + } + + int getSize() { + return size; + } + + int fill(JaccSymbol ajaccsymbol[], int i) { + return fill(ajaccsymbol, i, root); + } + + protected static class Node { + + Node left; + JaccSymbol data; + Node right; + + Node(JaccSymbol data) { + this.data = data; + } + } +} diff --git a/src/main/java/org/xbib/jacc/JaccTables.java b/src/main/java/org/xbib/jacc/JaccTables.java new file mode 100644 index 0000000..d697a27 --- /dev/null +++ b/src/main/java/org/xbib/jacc/JaccTables.java @@ -0,0 +1,191 @@ +package org.xbib.jacc; + +import org.xbib.jacc.grammar.LookaheadMachine; +import org.xbib.jacc.grammar.Resolver; +import org.xbib.jacc.grammar.Tables; + +import java.io.IOException; +import java.io.Writer; + +/** + * + */ +class JaccTables extends Tables { + + private String[] errors; + private int numErrors; + private int[][] index; + private int[] defaultRow; + + JaccTables(LookaheadMachine lookaheadmachine, Resolver resolver) { + super(lookaheadmachine, resolver); + errors = null; + numErrors = 0; + } + + int getNumErrors() { + return numErrors; + } + + String getError(int i) { + return errors[i]; + } + + boolean errorAt(int i, int j) { + return action[i][j - numNTs] == 0; + } + + String errorSet(int i, int j, String s) { + if (arg[i][j - numNTs] != 0) { + return errors[arg[i][j - numNTs] - 1]; + } else { + arg[i][j - numNTs] = errorNo(s) + 1; + return null; + } + } + + private int errorNo(String s) { + for (int i = 0; i < numErrors; i++) { + if (errors[i].equals(s)) { + return i; + } + } + String as[] = new String[numErrors != 0 ? 2 * numErrors : 1]; + System.arraycopy(errors, 0, as, 0, numErrors); + errors = as; + errors[numErrors] = s; + return numErrors++; + } + + void analyzeRows() { + if (index == null) { + RowAnalysis rowanalysis = new RowAnalysis(); + int i = machine.getNumStates(); + index = new int[i][]; + defaultRow = new int[i]; + for (int j = 0; j < i; j++) { + rowanalysis.analyze(j); + } + } + } + + int[] indexAt(int i) { + return index[i]; + } + + int getDefaultRowAt(int i) { + return defaultRow[i]; + } + + public void display(Writer writer) throws IOException { + int i = machine.getNumStates(); + for (int j = 0; j < i; j++) { + writer.write("state " + j + ":\n"); + for (int k = 0; k < numTs; k++) { + switch (action[j][k]) { + case 0: + writer.write(" E"); + break; + case 1: + writer.write(" S"); + break; + case 2: + writer.write(" R"); + break; + default: + break; + } + writer.write(arg[j][k]); + } + writer.write("\n"); + } + } + + /** + * + */ + private class RowAnalysis { + private byte[] a; + private int[] b; + private int size; + private int[] idx; + + void analyze(int i) { + a = action[i]; + b = arg[i]; + size = numTs; + idx = new int[size]; + for (int j = 0; j < numTs; j++) { + idx[j] = j; + } + for (int k = size / 2; k >= 0; k--) { + heapify(k); + } + for (int l = size - 1; l > 0; l--) { + int i1 = idx[l]; + idx[l] = idx[0]; + idx[0] = i1; + size--; + heapify(0); + } + index[i] = idx; + defaultRow[i] = findDefault(); + } + + private void heapify(int i) { + int j = i; + int k = idx[j]; + do { + int l = 2 * i + 1; + int i1 = l + 1; + if (l < size) { + int j1 = idx[l]; + if (a[j1] > a[k] || a[j1] == a[k] && b[j1] > b[k]) { + j = l; + k = j1; + } + if (i1 < size) { + int k1 = idx[i1]; + if (a[k1] > a[k] || a[k1] == a[k] && b[k1] > b[k]) { + j = i1; + k = k1; + } + } + } + if (j == i) { + return; + } + idx[j] = idx[i]; + idx[i] = k; + i = j; + k = idx[j]; + } while (true); + } + + int findDefault() { + int i = 1; + int j = -1; + int k = 0; + do { + if (k >= a.length) { + break; + } + int l = idx[k]; + byte byte0 = a[l]; + if (byte0 == 1) { + k++; + } else { + int j1 = 1; + for (int k1 = b[l]; ++k < a.length && a[idx[k]] == byte0 && b[idx[k]] == k1; ) { + j1++; + } + if (j1 > i) { + j = l; + i = j1; + } + } + } while (true); + return j; + } + } +} diff --git a/src/main/java/org/xbib/jacc/JaccTokens.java b/src/main/java/org/xbib/jacc/JaccTokens.java new file mode 100644 index 0000000..fd0f93b --- /dev/null +++ b/src/main/java/org/xbib/jacc/JaccTokens.java @@ -0,0 +1,36 @@ +package org.xbib.jacc; + +interface JaccTokens { + int ERROR = -1; + int ENDINPUT = 0; + int MARK = 1; + int CODE = 2; + int IDENT = 3; + int CHARLIT = 4; + int STRLIT = 5; + int INTLIT = 6; + int ACTION = 7; + int TOKEN = 8; + int TYPE = 9; + int PREC = 10; + int LEFT = 11; + int RIGHT = 12; + int NONASSOC = 13; + int START = 14; + int PACKAGE = 15; + int CLASS = 16; + int INTERFACE = 17; + int EXTENDS = 18; + int IMPLEMENTS = 19; + int SEMANTIC = 20; + int GETTOKEN = 21; + int NEXTTOKEN = 22; + int COLON = 58; + int SEMI = 59; + int BAR = 124; + int TOPEN = 60; + int TCLOSE = 62; + int BOPEN = 91; + int BCLOSE = 93; + int DOT = 46; +} diff --git a/src/main/java/org/xbib/jacc/NamedJaccSymbols.java b/src/main/java/org/xbib/jacc/NamedJaccSymbols.java new file mode 100644 index 0000000..d448b39 --- /dev/null +++ b/src/main/java/org/xbib/jacc/NamedJaccSymbols.java @@ -0,0 +1,53 @@ +package org.xbib.jacc; + +/** + * + */ +class NamedJaccSymbols extends JaccSymbols { + + JaccSymbol find(String s) { + for (JaccSymbols.Node node = root; node != null; ) { + int i = s.compareTo(node.data.getName()); + if (i < 0) { + node = node.left; + } else if (i > 0) { + node = node.right; + } else { + return node.data; + } + } + return null; + } + + JaccSymbol findOrAdd(String s) { + if (root == null) { + JaccSymbol jaccsymbol = new JaccSymbol(s); + root = new JaccSymbols.Node(jaccsymbol); + size++; + return jaccsymbol; + } + JaccSymbols.Node node = root; + do { + int i = s.compareTo(node.data.getName()); + if (i < 0) { + if (node.left == null) { + JaccSymbol jaccsymbol1 = new JaccSymbol(s); + node.left = new JaccSymbols.Node(jaccsymbol1); + size++; + return jaccsymbol1; + } + node = node.left; + } else if (i > 0) { + if (node.right == null) { + JaccSymbol jaccsymbol2 = new JaccSymbol(s); + node.right = new JaccSymbols.Node(jaccsymbol2); + size++; + return jaccsymbol2; + } + node = node.right; + } else { + return node.data; + } + } while (true); + } +} diff --git a/src/main/java/org/xbib/jacc/NumJaccSymbols.java b/src/main/java/org/xbib/jacc/NumJaccSymbols.java new file mode 100644 index 0000000..f57b820 --- /dev/null +++ b/src/main/java/org/xbib/jacc/NumJaccSymbols.java @@ -0,0 +1,53 @@ +package org.xbib.jacc; + +class NumJaccSymbols extends JaccSymbols { + + JaccSymbol find(int i) { + Node node = root; + while (node != null) { + int j = node.data.getNum(); + if (i < j) { + node = node.left; + } else { + if (i > j) { + node = node.right; + } else { + return node.data; + } + } + } + return null; + } + + JaccSymbol findOrAdd(String s, int i) { + if (root == null) { + JaccSymbol jaccsymbol = new JaccSymbol(s, i); + root = new JaccSymbols.Node(jaccsymbol); + size++; + return jaccsymbol; + } + JaccSymbols.Node node = root; + do { + int j = node.data.getNum(); + if (i < j) { + if (node.left == null) { + JaccSymbol jaccsymbol1 = new JaccSymbol(s, i); + node.left = new JaccSymbols.Node(jaccsymbol1); + size++; + return jaccsymbol1; + } + node = node.left; + } else if (i > j) { + if (node.right == null) { + JaccSymbol jaccsymbol2 = new JaccSymbol(s, i); + node.right = new JaccSymbols.Node(jaccsymbol2); + size++; + return jaccsymbol2; + } + node = node.right; + } else { + return node.data; + } + } while (true); + } +} diff --git a/src/main/java/org/xbib/jacc/Output.java b/src/main/java/org/xbib/jacc/Output.java new file mode 100644 index 0000000..37fe7c9 --- /dev/null +++ b/src/main/java/org/xbib/jacc/Output.java @@ -0,0 +1,85 @@ +package org.xbib.jacc; + +import org.xbib.jacc.compiler.Failure; +import org.xbib.jacc.compiler.Handler; +import org.xbib.jacc.compiler.Phase; +import org.xbib.jacc.grammar.Grammar; +import org.xbib.jacc.grammar.Machine; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.Writer; +import java.nio.charset.StandardCharsets; +import java.time.LocalDateTime; + +/** + * + */ +abstract class Output extends Phase { + + protected Grammar grammar; + protected int numTs; + protected int numNTs; + protected Machine machine; + int numSyms; + int numStates; + JaccTables tables; + JaccResolver resolver; + Settings settings; + + Output(Handler handler, JaccJob jaccjob) { + super(handler); + tables = jaccjob.getTables(); + machine = tables.getMachine(); + grammar = machine.getGrammar(); + numTs = grammar.getNumTs(); + numNTs = grammar.getNumNTs(); + numSyms = grammar.getNumSyms(); + numStates = machine.getNumStates(); + resolver = jaccjob.getResolver(); + settings = jaccjob.getSettings(); + } + + static void indent(Writer writer, int i, String[] as) throws IOException { + for (String a : as) { + indent(writer, i, a); + } + } + + static void indent(Writer writer, int i) throws IOException { + for (int j = 0; j < i; j++) { + writer.write(" "); + } + } + + static void indent(Writer writer, int i, String s) throws IOException { + indent(writer, i); + writer.write(s + "\n"); + } + + static void datestamp(Writer writer) throws IOException { + writer.write("// Output created by jacc 2.1.0 on " + LocalDateTime.now() + "\n"); + } + + public void write(String s) throws IOException { + Writer writer = null; + try { + File file = new File(s); + File parent = file.getParentFile(); + if (!parent.exists()) { + boolean mkdirs = parent.mkdirs(); + } + writer = new OutputStreamWriter(new FileOutputStream(s), StandardCharsets.UTF_8); + write(writer); + } catch (IOException ioexception) { + report(new Failure("Cannot write to file \"" + s + "\"")); + } + if (writer != null) { + writer.close(); + } + } + + public abstract void write(Writer writer) throws IOException; +} diff --git a/src/main/java/org/xbib/jacc/ParserOutput.java b/src/main/java/org/xbib/jacc/ParserOutput.java new file mode 100644 index 0000000..1c9d0d9 --- /dev/null +++ b/src/main/java/org/xbib/jacc/ParserOutput.java @@ -0,0 +1,498 @@ +package org.xbib.jacc; + +import org.xbib.jacc.compiler.Failure; +import org.xbib.jacc.compiler.Handler; +import org.xbib.jacc.grammar.Grammar; + +import java.io.IOException; +import java.io.Writer; + +/** + * + */ +class ParserOutput extends Output { + + private int yyaccept; + private int yyabort; + private int stackOverflow; + private int errorHandler; + private int userErrorHandler; + private int[] stNumSwitches; + private int[][] ntGoto; + private int[][] ntGotoSrc; + private int[] ntDefault; + private int[] ntDistinct; + private int errTok; + private boolean errMsgs; + private boolean errUsed; + + ParserOutput(Handler handler, JaccJob jaccjob) { + super(handler, jaccjob); + errMsgs = false; + errUsed = false; + tables.analyzeRows(); + } + + public void write(Writer writer) throws IOException { + datestamp(writer); + String s = settings.getPackageName(); + if (s != null) { + writer.write("package " + s + ";\n"); + } + if (settings.getPreText() != null) { + writer.write(settings.getPreText() + "\n"); + } + yyaccept = 2 * numStates; + stackOverflow = 2 * numStates + 1; + yyabort = 2 * numStates + 2; + errorHandler = 2 * numStates + 3; + userErrorHandler = 2 * numStates + 4; + int ai[] = new int[numNTs]; + stNumSwitches = new int[numStates]; + for (int i = 0; i < numStates; i++) { + int ai1[] = machine.getGotosAt(i); + for (int anAi1 : ai1) { + ai[machine.getEntry(anAi1)]++; + } + byte abyte0[] = tables.getActionAt(i); + int ai4[] = tables.getArgAt(i); + int l3 = tables.getDefaultRowAt(i); + stNumSwitches[i] = 0; + for (int j4 = 0; j4 < abyte0.length; j4++) { + if (l3 < 0 || abyte0[j4] != abyte0[l3] || ai4[j4] != ai4[l3]) { + stNumSwitches[i]++; + } + } + } + ntGoto = new int[numNTs][]; + ntGotoSrc = new int[numNTs][]; + ntDefault = new int[numNTs]; + ntDistinct = new int[numNTs]; + for (int j = 0; j < numNTs; j++) { + ntGoto[j] = new int[ai[j]]; + ntGotoSrc[j] = new int[ai[j]]; + } + for (int k = 0; k < numStates; k++) { + int ai2[] = machine.getGotosAt(k); + for (int anAi2 : ai2) { + int j3 = machine.getEntry(anAi2); + ntGoto[j3][--ai[j3]] = anAi2; + ntGotoSrc[j3][ai[j3]] = k; + } + } + for (int l = 0; l < numNTs; l++) { + int l1 = -1; + int k2 = 0; + int k3 = ntGoto[l].length; + for (int i4 = 0; i4 + k2 < k3; i4++) { + int k4 = 1; + for (int l4 = i4 + 1; l4 < k3; l4++) { + if (ntGoto[l][l4] == ntGoto[l][i4]) { + k4++; + } + } + if (k4 > k2) { + k2 = k4; + l1 = i4; + } + } + ntDefault[l] = l1; + ntDistinct[l] = ntGoto[l].length - (k2 - 1); + } + errMsgs = tables.getNumErrors() > 0; + errTok = numNTs; + while (errTok < numSyms && !grammar.getSymbol(errTok).getName().equals("error")) { + errTok++; + } + if (errTok < numSyms) { + for (int i1 = 0; i1 < numStates && !errUsed; i1++) { + int ai3[] = machine.getShiftsAt(i1); + for (int l2 = 0; l2 < ai3.length && !errUsed; l2++) { + if (machine.getEntry(ai3[l2]) == errTok) { + errUsed = true; + } + } + } + } + writer.write("public class " + settings.getClassName()); + if (settings.getExtendsName() != null) { + writer.write(" extends " + settings.getExtendsName()); + } + if (settings.getImplementsNames() != null) { + writer.write(" implements " + settings.getImplementsNames()); + } + writer.write(" {\n"); + indent(writer, 1, new String[]{ + "private int yyss = 100;", + "private int yytok;", + "private int yysp = 0;", + "private int[] yyst;", + "protected int yyerrno = (-1);" + }); + if (errUsed) { + indent(writer, 1, "private int yyerrstatus = 3;"); + } + indent(writer, 1, "private " + settings.getTypeName() + "[] yysv;"); + indent(writer, 1, "private " + settings.getTypeName() + " yyrv;"); + writer.write("\n"); + defineParse(writer, 1); + defineExpand(writer, 1); + defineErrRec(writer, 1); + for (int j1 = 0; j1 < numStates; j1++) { + defineState(writer, 1, j1); + } + for (int k1 = 0; k1 < numNTs; k1++) { + Grammar.Prod aprod[] = grammar.getProds(k1); + for (Grammar.Prod anAprod : aprod) { + defineReduce(writer, 1, anAprod, k1); + } + defineNonterminal(writer, 1, k1); + } + defineErrMsgs(writer); + if (settings.getPostText() != null) { + writer.write(settings.getPostText() + "\n"); + } + writer.write("}\n"); + } + + private void defineErrMsgs(Writer writer) throws IOException { + if (errMsgs) { + indent(writer, 1, new String[]{ + "private int yyerr(int e, int n) {", " yyerrno = e;", " return n;", "}" + }); + } + indent(writer, 1, "protected String[] yyerrmsgs = {"); + int i = tables.getNumErrors(); + if (i > 0) { + for (int j = 0; j < i - 1; j++) { + indent(writer, 2, "\"" + tables.getError(j) + "\","); + } + indent(writer, 2, "\"" + tables.getError(i - 1) + "\""); + } + indent(writer, 1, "};"); + } + + private void defineExpand(Writer writer, int i) throws IOException { + indent(writer, i, new String[]{ + "protected void yyexpand() {", " int[] newyyst = new int[2*yyst.length];" + }); + indent(writer, i + 1, settings.getTypeName() + "[] newyysv = new " + settings.getTypeName() + "[2*yyst.length];"); + indent(writer, i, new String[]{ + " for (int i=0; i=yyst.length) {", " yyexpand();", "}" + }); + indent(writer, i, "case " + (j + numStates) + ":"); + if (stNumSwitches[j] > 5) { + continueTo(writer, i + 1, "yys" + j + "()", true); + } else { + switchState(writer, i + 1, j, true); + } + writer.write("\n"); + } + + private void continueTo(Writer writer, int i, String s, boolean flag) throws IOException { + if (flag) { + indent(writer, i, "yyn = " + s + ";"); + indent(writer, i, "continue;"); + } else { + indent(writer, i, "return " + s + ";"); + } + } + + private void defineState(Writer writer, int i, int j) throws IOException { + if (stNumSwitches[j] > 5) { + indent(writer, i, "private int yys" + j + "() {"); + switchState(writer, i + 1, j, false); + indent(writer, i, "}"); + writer.write("\n"); + } + } + + private void switchState(Writer writer, int i, int j, boolean flag) throws IOException { + byte[] abyte0 = tables.getActionAt(j); + int[] ai = tables.getArgAt(j); + int k = tables.getDefaultRowAt(j); + if (stNumSwitches[j] > 0) { + indent(writer, i, "switch (yytok) {"); + int ai1[] = tables.indexAt(j); + int k1; + for (int l = 0; l < ai1.length; l = k1) { + int i1 = ai1[l]; + byte byte0 = abyte0[i1]; + int j1 = ai[i1]; + k1 = l; + ++k1; + while (k1 < ai1.length && abyte0[ai1[k1]] == byte0 && ai[ai1[k1]] == j1) { + k1++; + } + if (k >= 0 && byte0 == abyte0[k] && j1 == ai[k]) { + continue; + } + for (int l1 = l; l1 < k1; l1++) { + indent(writer, i + 1); + writer.write("case "); + if (ai1[l1] == numTs - 1) { + writer.write("ENDINPUT"); + } else { + writer.write(grammar.getTerminal(ai1[l1]).getName()); + } + writer.write(":\n"); + } + continueTo(writer, i + 2, codeAction(j, byte0, j1), flag); + } + indent(writer, i, "}"); + } + if (k < 0) { + continueTo(writer, i, Integer.toString(errorHandler), flag); + } else { + continueTo(writer, i, codeAction(j, abyte0[k], ai[k]), flag); + } + } + + private String codeAction(int i, int j, int k) { + if (j == 0) { + String s = Integer.toString(errorHandler); + return k != 0 ? "yyerr(" + (k - 1) + ", " + s + ")" : s; + } + if (j == 2) { + return "yyr" + machine.reduceItem(i, k).getSeqNo() + "()"; + } else { + return Integer.toString(k >= 0 ? k : yyaccept); + } + } + + private void defineReduce(Writer writer, int i, Grammar.Prod prod, int j) throws IOException { + if ((prod instanceof JaccProd) && ntDefault[j] >= 0) { + JaccProd jaccprod = (JaccProd) prod; + indent(writer, i); + writer.write("private int yyr" + jaccprod.getSeqNo() + "() { // "); + writer.write(grammar.getSymbol(j).getName() + " : "); + writer.write(grammar.displaySymbols(jaccprod.getRhs(), "/* empty */", " ") + "\n"); + String s = jaccprod.getAction(); + int k = jaccprod.getRhs().length; + if (s != null) { + indent(writer, i + 1); + translateAction(writer, jaccprod, s); + indent(writer, i + 1, "yysv[yysp-=" + k + "] = yyrv;"); + } else if (k > 0) { + indent(writer, i + 1, "yysp -= " + k + ";"); + } + gotoNonterminal(writer, i + 1, j); + indent(writer, i, "}"); + writer.write("\n"); + } + } + + private void translateAction(Writer writer, JaccProd jaccprod, String s) throws IOException { + int ai[] = jaccprod.getRhs(); + int i = s.length(); + for (int j = 0; j < i; j++) { + char c = s.charAt(j); + if (c == '$') { + c = s.charAt(j + 1); + if (c == '$') { + j++; + writer.write("yyrv"); + continue; + } + if (Character.isDigit(c)) { + int k = 0; + do { + k = k * 10 + Character.digit(c, 10); + j++; + c = s.charAt(j + 1); + } while (Character.isDigit(c)); + if (k < 1 || k > ai.length) { + report(new Failure(jaccprod.getActionPos(), "$" + k + " cannot be used in this action.")); + continue; + } + int l = (1 + ai.length) - k; + String s1 = null; + if (grammar.getSymbol(ai[k - 1]) instanceof JaccSymbol) { + JaccSymbol jaccsymbol = (JaccSymbol) grammar.getSymbol(ai[k - 1]); + s1 = jaccsymbol.getType(); + } + if (s1 != null) { + writer.write("((" + s1 + ")"); + } + writer.write("yysv[yysp-" + l + "]"); + if (s1 != null) { + writer.write(")"); + } + } else { + writer.write('$'); + } + continue; + } + if (c == '\n') { + writer.write("\n"); + } else { + writer.write(c); + } + } + writer.write("\n"); + } + + private void gotoNonterminal(Writer writer, int i, int j) throws IOException { + if (ntDefault[j] < 0) { + return; + } + if (ntDistinct[j] == 1) { + indent(writer, i, "return " + ntGoto[j][0] + ";"); + } else { + if (grammar.getProds(j).length == 1) { + nonterminalSwitch(writer, i, j); + } else { + indent(writer, i, "return " + ntName(j) + "();"); + } + } + } + + private void defineNonterminal(Writer writer, int i, int j) throws IOException { + if (ntDefault[j] >= 0 && ntDistinct[j] != 1 && grammar.getProds(j).length != 1) { + indent(writer, i, "private int " + ntName(j) + "() {"); + nonterminalSwitch(writer, i + 1, j); + indent(writer, i, "}"); + writer.write("\n"); + } + } + + private void nonterminalSwitch(Writer writer, int i, int j) throws IOException { + int k = ntGoto[j][ntDefault[j]]; + indent(writer, i); + writer.write("switch (yyst[yysp-1]) {\n"); + for (int l = 0; l < ntGoto[j].length; l++) { + int i1 = ntGoto[j][l]; + if (i1 != k) { + indent(writer, i + 1); + writer.write("case " + ntGotoSrc[j][l]); + writer.write(": return " + i1 + ";\n"); + } + } + indent(writer, i + 1); + writer.write("default: return " + k + ";\n"); + indent(writer, i); + writer.write("}\n"); + } + + private String ntName(int i) { + return "yyp" + grammar.getSymbol(i).getName(); + } + + private void errorCases(Writer writer, int i) throws IOException { + indent(writer, i, "case " + errorHandler + ":"); + if (!errUsed) { + indent(writer, i + 1, new String[]{ + "yyerror(\"syntax error\");", "return false;" + }); + return; + } + indent(writer, i + 1, new String[]{ + "if (yyerrstatus>2) {", " yyerror(\"syntax error\");", "}" + }); + indent(writer, i, "case " + userErrorHandler + " :"); + indent(writer, i + 1, new String[]{ + "if (yyerrstatus==0) {", + " if ((" + settings.getGetToken(), + " )==ENDINPUT) {", + " return false;", + " }", + " " + settings.getNextToken(), + " ;" + }); + indent(writer, i + 2, "yyn = " + numStates + " + yyst[yysp-1];"); + indent(writer, i + 1, new String[]{ + " continue;", + "} else {", + " yyerrstatus = 0;", + " while (yysp>0) {", + " switch(yyst[yysp-1]) {" + }); + for (int j = 0; j < numStates; j++) { + int[] ai = machine.getShiftsAt(j); + for (int anAi : ai) { + if (machine.getEntry(anAi) == errTok) { + indent(writer, i + 4, "case " + j + ":"); + indent(writer, i + 5, "yyn = " + anAi + ";"); + indent(writer, i + 5, "continue loop;"); + } + } + } + indent(writer, i + 1, new String[]{ + " }", " yysp--;", " }", " return false;", "}" + }); + } +} diff --git a/src/main/java/org/xbib/jacc/Settings.java b/src/main/java/org/xbib/jacc/Settings.java new file mode 100644 index 0000000..85b24c0 --- /dev/null +++ b/src/main/java/org/xbib/jacc/Settings.java @@ -0,0 +1,174 @@ +package org.xbib.jacc; + +import org.xbib.jacc.grammar.Grammar; +import org.xbib.jacc.grammar.LALRMachine; +import org.xbib.jacc.grammar.LR0Machine; +import org.xbib.jacc.grammar.LookaheadMachine; +import org.xbib.jacc.grammar.SLRMachine; + +/** + * + */ +class Settings { + + private static final int LR0 = 0; + private static final int SLR1 = 1; + private static final int LALR1 = 2; + private int machineType; + private String packageName; + private String className; + private String interfaceName; + private String extendsName; + private String implementsNames; + private String typeName; + private String getToken; + private String nextToken; + private String getSemantic; + private StringBuilder preTextBuffer; + private StringBuilder postTextBuffer; + + Settings() { + machineType = LALR1; + preTextBuffer = new StringBuilder(); + postTextBuffer = new StringBuilder(); + } + + public int getMachineType() { + return machineType; + } + + void setMachineType(int i) { + machineType = i; + } + + LookaheadMachine makeMachine(Grammar grammar) { + if (machineType == LR0) { + return new LR0Machine(grammar); + } + if (machineType == SLR1) { + return new SLRMachine(grammar); + } else { + return new LALRMachine(grammar); + } + } + + String getPackageName() { + return packageName; + } + + void setPackageName(String s) { + packageName = s; + } + + String getClassName() { + return className; + } + + void setClassName(String s) { + className = s; + } + + String getInterfaceName() { + return interfaceName; + } + + void setInterfaceName(String s) { + interfaceName = s; + } + + String getExtendsName() { + return extendsName; + } + + void setExtendsName(String s) { + extendsName = s; + } + + void addImplementsNames(String s) { + if (implementsNames != null) { + implementsNames += ", " + s; + } else { + implementsNames = s; + } + } + + String getImplementsNames() { + return implementsNames; + } + + public void setImplementsNames(String s) { + implementsNames = s; + } + + String getTypeName() { + return typeName; + } + + void setTypeName(String s) { + typeName = s; + } + + String getGetToken() { + return getToken; + } + + void setGetToken(String s) { + getToken = s; + } + + String getNextToken() { + return nextToken; + } + + void setNextToken(String s) { + nextToken = s; + } + + String getGetSemantic() { + return getSemantic; + } + + void setGetSemantic(String s) { + getSemantic = s; + } + + void addPreText(String s) { + preTextBuffer.append(s); + } + + String getPreText() { + return preTextBuffer.toString(); + } + + void addPostText(String s) { + postTextBuffer.append(s); + } + + String getPostText() { + return postTextBuffer.toString(); + } + + void fillBlanks(String s) { + if (getClassName() == null) { + setClassName(s + "Parser"); + } + if (getInterfaceName() == null) { + setInterfaceName(s + "Tokens"); + } + if (getTypeName() == null) { + setTypeName("Object"); + } + if (getInterfaceName() != null) { + addImplementsNames(getInterfaceName()); + } + if (getGetSemantic() == null) { + setGetSemantic("lexer.getSemantic()"); + } + if (getGetToken() == null) { + setGetToken("lexer.getToken()"); + } + if (getNextToken() == null) { + setNextToken("lexer.nextToken()"); + } + } +} diff --git a/src/main/java/org/xbib/jacc/TextOutput.java b/src/main/java/org/xbib/jacc/TextOutput.java new file mode 100644 index 0000000..832fa23 --- /dev/null +++ b/src/main/java/org/xbib/jacc/TextOutput.java @@ -0,0 +1,128 @@ +package org.xbib.jacc; + +import org.xbib.jacc.compiler.Handler; +import org.xbib.jacc.util.IntSet; + +import java.io.IOException; +import java.io.Writer; + +/** + * + */ +class TextOutput extends Output { + + private boolean wantFirst; + + TextOutput(Handler handler, JaccJob jaccjob, boolean flag) { + super(handler, jaccjob); + wantFirst = false; + wantFirst = flag; + tables.analyzeRows(); + } + + public void write(Writer writer) throws IOException { + datestamp(writer); + for (int i = 0; i < numStates; i++) { + writer.write(resolver.getConflictsAt(i)); + writer.write(describeEntry(i) + "\n"); + IntSet intset = machine.getItemsAt(i); + int k = intset.size(); + for (int i1 = 0; i1 < k; i1++) { + indent(writer, 1); + machine.getItems().getItem(intset.at(i1)).display(writer); + writer.write("\n"); + } + writer.write("\n"); + byte abyte0[] = tables.getActionAt(i); + int ai1[] = tables.getArgAt(i); + int j1 = tables.getDefaultRowAt(i); + int ai2[] = tables.indexAt(i); + for (int k1 = 0; k1 < abyte0.length; k1++) { + int l1 = ai2[k1]; + if (j1 < 0 || abyte0[l1] != abyte0[j1] || ai1[l1] != ai1[j1]) { + indent(writer, 1); + writer.write(grammar.getTerminal(l1).getName()); + writer.write(' '); + writer.write(describeAction(i, abyte0[l1], ai1[l1]) + "\n"); + } + } + indent(writer, 1); + if (j1 < 0) { + writer.write(". error" + "\n"); + } else { + writer.write(". "); + writer.write(describeAction(i, abyte0[j1], ai1[j1]) + "\n"); + } + writer.write("\n"); + int ai3[] = machine.getGotosAt(i); + if (ai3.length <= 0) { + continue; + } + for (int anAi3 : ai3) { + int j2 = machine.getEntry(anAi3); + indent(writer, 1); + writer.write(grammar.getSymbol(j2).getName()); + writer.write(" " + describeGoto(anAi3) + "\n"); + } + writer.write("\n"); + } + + if (wantFirst) { + grammar.getFirst().display(writer); + writer.write("\n"); + grammar.getFollow().display(writer); + writer.write("\n"); + grammar.getNullable().display(writer); + writer.write("\n"); + } + if (tables.getProdUnused() > 0) { + for (int j = 0; j < numNTs; j++) { + boolean aflag[] = tables.getProdsUsedAt(j); + for (int l = 0; l < aflag.length; l++) { + if (!aflag[l]) { + int ai[] = grammar.getProds(j)[l].getRhs(); + writer.write("Rule not reduced: "); + writer.write(grammar.getNonterminal(j).getName()); + writer.write(" : "); + writer.write(grammar.displaySymbols(ai, "", " ") + "\n"); + } + } + } + writer.write("\n"); + } + writer.write(numTs + " terminals, " + numNTs + " nonterminals;"); + writer.write(grammar.getNumProds() + " grammar rules, " + numStates + " states;"); + writer.write(resolver.getNumSRConflicts() + " shift/reduce and " + + resolver.getNumRRConflicts() + " reduce/reduce conflicts reported."); + } + + private String describeEntry(int i) { + return "state " + i + " (entry on " + grammar.getSymbol(machine.getEntry(i)) + ")"; + } + + private String describeAction(int i, int j, int k) { + if (j == 0) { + if (k == 0) { + return "error"; + } else { + return "error \"" + tables.getError(k - 1) + "\""; + } + } + if (j == 2) { + return "reduce " + machine.reduceItem(i, k).getSeqNo(); + } + if (k < 0) { + return "accept"; + } else { + return describeShift(k); + } + } + + private String describeShift(int i) { + return "shift " + i; + } + + private String describeGoto(int i) { + return "goto " + i; + } +} diff --git a/src/main/java/org/xbib/jacc/TokensOutput.java b/src/main/java/org/xbib/jacc/TokensOutput.java new file mode 100644 index 0000000..3a60e98 --- /dev/null +++ b/src/main/java/org/xbib/jacc/TokensOutput.java @@ -0,0 +1,44 @@ +package org.xbib.jacc; + +import org.xbib.jacc.compiler.Handler; +import org.xbib.jacc.grammar.Grammar; + +import java.io.IOException; +import java.io.Writer; + +/** + * + */ +class TokensOutput extends Output { + + TokensOutput(Handler handler, JaccJob jaccjob) { + super(handler, jaccjob); + } + + @Override + public void write(Writer writer) throws IOException { + datestamp(writer); + String s = settings.getPackageName(); + if (s != null) { + writer.write("package " + s + ";\n\n"); + } + writer.write("interface " + settings.getInterfaceName() + " {\n"); + indent(writer, 1); + writer.write("int ENDINPUT = 0;\n"); + for (int i = 0; i < numTs - 1; i++) { + Grammar.Symbol symbol = grammar.getTerminal(i); + if (!(symbol instanceof JaccSymbol)) { + continue; + } + JaccSymbol jaccsymbol = (JaccSymbol) symbol; + String s1 = jaccsymbol.getName(); + indent(writer, 1); + if (s1.startsWith("'")) { + writer.write("// " + s1 + " (code=" + jaccsymbol.getNum() + ")\n"); + } else { + writer.write("int " + s1 + " = " + jaccsymbol.getNum() + ";\n"); + } + } + writer.write("}\n"); + } +} diff --git a/src/main/java/org/xbib/jacc/compiler/ConsoleHandler.java b/src/main/java/org/xbib/jacc/compiler/ConsoleHandler.java new file mode 100644 index 0000000..6ac588b --- /dev/null +++ b/src/main/java/org/xbib/jacc/compiler/ConsoleHandler.java @@ -0,0 +1,23 @@ +package org.xbib.jacc.compiler; + +/** + * + */ +public class ConsoleHandler extends Handler { + protected void respondTo(Diagnostic diagnostic) { + Position position = diagnostic.getPos(); + if (diagnostic instanceof Warning) { + System.err.print("WARNING: "); + } else { + System.err.print("ERROR: "); + } + if (position != null) { + System.err.println(position.describe()); + } + String s = diagnostic.getText(); + if (s != null) { + System.err.println(s); + } + System.err.println(); + } +} diff --git a/src/main/java/org/xbib/jacc/compiler/Diagnostic.java b/src/main/java/org/xbib/jacc/compiler/Diagnostic.java new file mode 100644 index 0000000..a292061 --- /dev/null +++ b/src/main/java/org/xbib/jacc/compiler/Diagnostic.java @@ -0,0 +1,27 @@ +package org.xbib.jacc.compiler; + +/** + * + */ +abstract class Diagnostic extends Exception { + + private String text; + private Position position; + + Diagnostic(String s) { + text = s; + } + + Diagnostic(Position position, String s) { + this.position = position; + text = s; + } + + String getText() { + return text; + } + + Position getPos() { + return position; + } +} diff --git a/src/main/java/org/xbib/jacc/compiler/Failure.java b/src/main/java/org/xbib/jacc/compiler/Failure.java new file mode 100644 index 0000000..1d7e747 --- /dev/null +++ b/src/main/java/org/xbib/jacc/compiler/Failure.java @@ -0,0 +1,15 @@ +package org.xbib.jacc.compiler; + +/** + * + */ +public class Failure extends Diagnostic { + + public Failure(String s) { + super(s); + } + + public Failure(Position position, String s) { + super(position, s); + } +} diff --git a/src/main/java/org/xbib/jacc/compiler/Handler.java b/src/main/java/org/xbib/jacc/compiler/Handler.java new file mode 100644 index 0000000..a4ecc67 --- /dev/null +++ b/src/main/java/org/xbib/jacc/compiler/Handler.java @@ -0,0 +1,27 @@ +package org.xbib.jacc.compiler; + +/** + * + */ +public abstract class Handler { + + private int numFailures; + + public Handler() { + numFailures = 0; + } + + public int getNumFailures() { + return numFailures; + } + + void report(Diagnostic diagnostic) { + if (diagnostic instanceof Failure) { + numFailures++; + } + respondTo(diagnostic); + } + + protected abstract void respondTo(Diagnostic diagnostic); + +} diff --git a/src/main/java/org/xbib/jacc/compiler/JavaSource.java b/src/main/java/org/xbib/jacc/compiler/JavaSource.java new file mode 100644 index 0000000..ddaf79e --- /dev/null +++ b/src/main/java/org/xbib/jacc/compiler/JavaSource.java @@ -0,0 +1,121 @@ +package org.xbib.jacc.compiler; + +import java.io.IOException; +import java.io.Reader; + +/** + * + */ +public class JavaSource extends Source { + + private final StringBuilder buf; + private Reader input; + private int tabwidth; + private String description; + private int c0; + private int c1; + private int lineNumber; + + public JavaSource(Handler handler, String s, Reader reader) { + this(handler, s, reader, 8); + } + + private JavaSource(Handler handler, String s, Reader reader, int i) { + super(handler); + c1 = 0; + lineNumber = 0; + description = s; + input = reader; + tabwidth = i; + buf = new StringBuilder(); + } + + public String describe() { + return description; + } + + private void skip() throws IOException { + c0 = c1; + if (c0 != -1) { + c1 = input.read(); + if (c0 == 26 && c1 == -1) { + c0 = c1; + } + } + } + + @Override + public String readLine() throws IOException { + if (input == null) { + return null; + } + buf.setLength(0); + if (lineNumber++ == 0) { + skip(); + skip(); + } + if (c0 == -1) { + return null; + } + do { + if (c0 == -1 || c0 == 10 || c0 == 13) { + break; + } + if (c0 == 92) { + skip(); + if (c0 == 117) { + do { + skip(); + } while (c0 == 117); + int i = 0; + int k = 0; + for (int l; k < 4 && c0 != -1 && (l = Character.digit((char) c0, 16)) >= 0; ) { + i = (i << 4) + l; + k++; + skip(); + } + if (k != 4) { + report(new Warning("Error in Unicode escape sequence")); + } else { + buf.append((char) i); + } + continue; + } + buf.append('\\'); + if (c0 == -1) { + break; + } + buf.append((char) c0); + skip(); + } else if (c0 == 9 && tabwidth > 0) { + for (int j = tabwidth - buf.length() % tabwidth; j > 0; j--) { + buf.append(' '); + } + + skip(); + } else { + buf.append((char) c0); + skip(); + } + } while (true); + if (c0 == 13) { + skip(); + } + if (c0 == 10) { + skip(); + } + return buf.toString(); + } + + public int getLineNo() { + return lineNumber; + } + + public void close() throws IOException { + if (input != null) { + input.close(); + input = null; + buf.setLength(0); + } + } +} diff --git a/src/main/java/org/xbib/jacc/compiler/Lexer.java b/src/main/java/org/xbib/jacc/compiler/Lexer.java new file mode 100644 index 0000000..800162f --- /dev/null +++ b/src/main/java/org/xbib/jacc/compiler/Lexer.java @@ -0,0 +1,40 @@ +package org.xbib.jacc.compiler; + +import java.io.IOException; + +/** + * + */ +public abstract class Lexer extends Phase { + + protected int token; + + protected String lexemeText; + + Lexer(Handler handler) { + super(handler); + } + + public abstract int nextToken() throws IOException; + + public int getToken() { + return token; + } + + public String getLexeme() { + return lexemeText; + } + + public abstract Position getPos(); + + public boolean match(int i) throws IOException { + if (i == token) { + nextToken(); + return true; + } else { + return false; + } + } + + public abstract void close() throws IOException; +} diff --git a/src/main/java/org/xbib/jacc/compiler/Phase.java b/src/main/java/org/xbib/jacc/compiler/Phase.java new file mode 100644 index 0000000..bfab006 --- /dev/null +++ b/src/main/java/org/xbib/jacc/compiler/Phase.java @@ -0,0 +1,23 @@ +package org.xbib.jacc.compiler; + +/** + * + */ +public abstract class Phase { + + private final Handler handler; + + protected Phase(Handler handler) { + this.handler = handler; + } + + public Handler getHandler() { + return handler; + } + + protected void report(Diagnostic diagnostic) { + if (handler != null) { + handler.report(diagnostic); + } + } +} diff --git a/src/main/java/org/xbib/jacc/compiler/Position.java b/src/main/java/org/xbib/jacc/compiler/Position.java new file mode 100644 index 0000000..f90ef5d --- /dev/null +++ b/src/main/java/org/xbib/jacc/compiler/Position.java @@ -0,0 +1,11 @@ +package org.xbib.jacc.compiler; + +/** + * + */ +public abstract class Position { + + public abstract String describe(); + + public abstract Position copy(); +} diff --git a/src/main/java/org/xbib/jacc/compiler/Source.java b/src/main/java/org/xbib/jacc/compiler/Source.java new file mode 100644 index 0000000..1c7bfc3 --- /dev/null +++ b/src/main/java/org/xbib/jacc/compiler/Source.java @@ -0,0 +1,26 @@ +package org.xbib.jacc.compiler; + +import java.io.IOException; + +/** + * + */ +public abstract class Source extends Phase { + + Source(Handler handler) { + super(handler); + } + + public abstract String describe(); + + public abstract String readLine() throws IOException; + + public abstract int getLineNo(); + + String getLine(int i) { + return null; + } + + public void close() throws IOException { + } +} diff --git a/src/main/java/org/xbib/jacc/compiler/SourceLexer.java b/src/main/java/org/xbib/jacc/compiler/SourceLexer.java new file mode 100644 index 0000000..395de6a --- /dev/null +++ b/src/main/java/org/xbib/jacc/compiler/SourceLexer.java @@ -0,0 +1,59 @@ +package org.xbib.jacc.compiler; + +import java.io.IOException; + +/** + * + */ +public abstract class SourceLexer extends Lexer { + + protected String line; + protected int col; + protected int c; + private Source source; + private SourcePosition pos; + + public SourceLexer(Handler handler, Source source) throws IOException { + super(handler); + col = -1; + this.source = source; + pos = new SourcePosition(source); + line = source.readLine(); + nextChar(); + } + + public Position getPos() { + return pos.copy(); + } + + protected void markPosition() { + pos.updateCoords(source.getLineNo(), col); + } + + protected void nextLine() throws IOException { + line = source.readLine(); + col = -1; + nextChar(); + } + + protected int nextChar() { + if (line == null) { + c = -1; + col = 0; + } else { + if (++col >= line.length()) { + c = 10; + } else { + c = line.charAt(col); + } + } + return c; + } + + public void close() throws IOException { + if (source != null) { + source.close(); + source = null; + } + } +} diff --git a/src/main/java/org/xbib/jacc/compiler/SourcePosition.java b/src/main/java/org/xbib/jacc/compiler/SourcePosition.java new file mode 100644 index 0000000..aae3771 --- /dev/null +++ b/src/main/java/org/xbib/jacc/compiler/SourcePosition.java @@ -0,0 +1,61 @@ +package org.xbib.jacc.compiler; + +/** + * + */ +class SourcePosition extends Position { + + private final Source source; + + private int row; + + private int column; + + SourcePosition(Source source) { + this(source, 0, 0); + } + + private SourcePosition(Source source, int i, int j) { + this.source = source; + row = i; + column = j; + } + + void updateCoords(int i, int j) { + row = i; + column = j; + } + + @Override + public String describe() { + StringBuilder sb = new StringBuilder(); + if (source != null) { + sb.append('"'); + sb.append(source.describe()); + sb.append('"'); + if (row > 0) { + sb.append(", "); + } + if (row > 0) { + sb.append("line "); + sb.append(row); + } + String s = source.getLine(row); + if (s != null) { + sb.append('\n'); + sb.append(s); + sb.append('\n'); + for (int i = 0; i < column; i++) { + sb.append(' '); + } + sb.append('^'); + } + } + return sb.length() > 0 ? sb.toString() : "input"; + } + + @Override + public Position copy() { + return new SourcePosition(source, row, column); + } +} diff --git a/src/main/java/org/xbib/jacc/compiler/Warning.java b/src/main/java/org/xbib/jacc/compiler/Warning.java new file mode 100644 index 0000000..04c6d29 --- /dev/null +++ b/src/main/java/org/xbib/jacc/compiler/Warning.java @@ -0,0 +1,15 @@ +package org.xbib.jacc.compiler; + +/** + * + */ +public class Warning extends Diagnostic { + + public Warning(String s) { + super(s); + } + + public Warning(Position position, String s) { + super(position, s); + } +} diff --git a/src/main/java/org/xbib/jacc/compiler/package-info.java b/src/main/java/org/xbib/jacc/compiler/package-info.java new file mode 100644 index 0000000..f2fb63b --- /dev/null +++ b/src/main/java/org/xbib/jacc/compiler/package-info.java @@ -0,0 +1,4 @@ +/** + * CLasses for compiler construction. + */ +package org.xbib.jacc.compiler; diff --git a/src/main/java/org/xbib/jacc/grammar/Analysis.java b/src/main/java/org/xbib/jacc/grammar/Analysis.java new file mode 100644 index 0000000..f2277de --- /dev/null +++ b/src/main/java/org/xbib/jacc/grammar/Analysis.java @@ -0,0 +1,38 @@ +package org.xbib.jacc.grammar; + +/** + * + */ +abstract class Analysis { + + private int[][] comps; + + Analysis(int[][] ai) { + comps = ai; + } + + void bottomUp() { + for (int[] comp : comps) { + analyzeComponent(comp); + } + } + + void topDown() { + for (int i = comps.length; i-- > 0; ) { + analyzeComponent(comps[i]); + } + } + + private void analyzeComponent(int ai[]) { + for (boolean flag = true; flag; ) { + flag = false; + int i = 0; + while (i < ai.length) { + flag |= analyze(ai[i]); + i++; + } + } + } + + protected abstract boolean analyze(int i); +} diff --git a/src/main/java/org/xbib/jacc/grammar/Finitary.java b/src/main/java/org/xbib/jacc/grammar/Finitary.java new file mode 100644 index 0000000..f1ad733 --- /dev/null +++ b/src/main/java/org/xbib/jacc/grammar/Finitary.java @@ -0,0 +1,77 @@ +package org.xbib.jacc.grammar; + +import java.io.IOException; +import java.io.Writer; + +/** + * + */ +public final class Finitary extends Analysis { + + private boolean[] finitary; + private boolean[] consider; + private Grammar grammar; + private int numNTs; + + Finitary(Grammar grammar) { + super(grammar.getComponents()); + this.grammar = grammar; + numNTs = grammar.getNumNTs(); + finitary = new boolean[numNTs]; + consider = new boolean[numNTs]; + for (int i = 0; i < numNTs; i++) { + finitary[i] = false; + consider[i] = true; + } + bottomUp(); + } + + protected boolean analyze(int i) { + boolean flag = false; + if (consider[i]) { + int j = 0; + Grammar.Prod[] aprod = grammar.getProds(i); + for (Grammar.Prod anAprod : aprod) { + int ai[] = anAprod.getRhs(); + int l; + l = 0; + while (l < ai.length && at(ai[l])) { + l++; + } + if (l >= ai.length) { + finitary[i] = true; + consider[i] = false; + flag = true; + break; + } + if (!consider[ai[l]]) { + j++; + } + } + if (j == aprod.length) { + consider[i] = false; + } + } + return flag; + } + + public boolean at(int i) { + return grammar.isTerminal(i) || finitary[i]; + } + + public void display(Writer writer) throws IOException { + writer.write("Finitary = {"); + int i = 0; + for (int j = 0; j < numNTs; j++) { + if (!at(j)) { + continue; + } + if (i > 0) { + writer.write(", "); + } + writer.write(grammar.getSymbol(j).getName()); + i++; + } + writer.write("}\n"); + } +} diff --git a/src/main/java/org/xbib/jacc/grammar/First.java b/src/main/java/org/xbib/jacc/grammar/First.java new file mode 100644 index 0000000..fdcef65 --- /dev/null +++ b/src/main/java/org/xbib/jacc/grammar/First.java @@ -0,0 +1,73 @@ +package org.xbib.jacc.grammar; + +import org.xbib.jacc.util.BitSet; + +import java.io.IOException; +import java.io.Writer; + +/** + * + */ +public class First extends Analysis { + + private Grammar grammar; + private Nullable nullable; + private int numNTs; + private int[][] first; + + First(Grammar grammar1, Nullable nullable1) { + super(grammar1.getComponents()); + grammar = grammar1; + nullable = nullable1; + numNTs = grammar1.getNumNTs(); + int numTs = grammar1.getNumTs(); + first = new int[numNTs][]; + for (int i = 0; i < numNTs; i++) { + first[i] = BitSet.make(numTs); + } + bottomUp(); + } + + protected boolean analyze(int i) { + boolean flag = false; + Grammar.Prod[] aprod = grammar.getProds(i); + label0: + for (Grammar.Prod anAprod : aprod) { + int[] ai = anAprod.getRhs(); + int k = 0; + do { + if (k >= ai.length) { + continue label0; + } + if (grammar.isTerminal(ai[k])) { + if (BitSet.addTo(first[i], ai[k] - numNTs)) { + flag = true; + } + continue label0; + } + if (BitSet.addTo(first[i], first[ai[k]])) { + flag = true; + } + if (!nullable.at(ai[k])) { + continue label0; + } + k++; + } while (true); + } + + return flag; + } + + public int[] at(int i) { + return first[i]; + } + + public void display(Writer writer) throws IOException { + writer.write("First sets:\n"); + for (int i = 0; i < first.length; i++) { + writer.write(" First(" + grammar.getSymbol(i) + "): {"); + writer.write(grammar.displaySymbolSet(at(i), numNTs)); + writer.write("}\n"); + } + } +} diff --git a/src/main/java/org/xbib/jacc/grammar/Follow.java b/src/main/java/org/xbib/jacc/grammar/Follow.java new file mode 100644 index 0000000..db8fc25 --- /dev/null +++ b/src/main/java/org/xbib/jacc/grammar/Follow.java @@ -0,0 +1,83 @@ +package org.xbib.jacc.grammar; + +import org.xbib.jacc.util.BitSet; + +import java.io.IOException; +import java.io.Writer; + +/** + * + */ +public class Follow extends Analysis { + + private Grammar grammar; + private Nullable nullable; + private First first; + private int numNTs; + private int[][] follow; + + Follow(Grammar grammar, Nullable nullable, First first1) { + super(grammar.getComponents()); + this.grammar = grammar; + this.nullable = nullable; + first = first1; + numNTs = grammar.getNumNTs(); + int numTs = grammar.getNumTs(); + follow = new int[numNTs][]; + for (int i = 0; i < numNTs; i++) { + follow[i] = BitSet.make(numTs); + } + BitSet.set(follow[0], numTs - 1); + topDown(); + } + + protected boolean analyze(int i) { + boolean flag = false; + Grammar.Prod aprod[] = grammar.getProds(i); + for (Grammar.Prod anAprod : aprod) { + int[] ai = anAprod.getRhs(); + for (int k = 0; k < ai.length; k++) { + if (!grammar.isNonterminal(ai[k])) { + continue; + } + int l = k + 1; + do { + if (l >= ai.length) { + break; + } + if (grammar.isTerminal(ai[l])) { + if (BitSet.addTo(follow[ai[k]], ai[l] - numNTs)) { + flag = true; + } + break; + } + if (BitSet.addTo(follow[ai[k]], first.at(ai[l]))) { + flag = true; + } + if (!nullable.at(ai[l])) { + break; + } + l++; + } while (true); + if (l >= ai.length && BitSet.addTo(follow[ai[k]], follow[i])) { + flag = true; + } + } + } + return flag; + } + + public int[] at(int i) { + return follow[i]; + } + + public void display(Writer writer) throws IOException { + writer.write("Follow sets:\n"); + for (int i = 0; i < follow.length; i++) { + writer.write(" Follow(" + grammar.getSymbol(i) + "): {"); + writer.write(grammar.displaySymbolSet(at(i), numNTs)); + writer.write("}\n"); + } + + } +} diff --git a/src/main/java/org/xbib/jacc/grammar/Grammar.java b/src/main/java/org/xbib/jacc/grammar/Grammar.java new file mode 100644 index 0000000..dc36d29 --- /dev/null +++ b/src/main/java/org/xbib/jacc/grammar/Grammar.java @@ -0,0 +1,282 @@ +package org.xbib.jacc.grammar; + +import org.xbib.jacc.util.BitSet; +import org.xbib.jacc.util.Interator; +import org.xbib.jacc.util.SCC; + +import java.io.IOException; +import java.io.Writer; + +/** + * + */ +public class Grammar { + private final Symbol[] symbols; + private final Prod[][] prods; + private int numSyms; + private int numNTs; + private int numTs; + private int[][] comps; + private int[][] depends; + private int[][] revdeps; + private Nullable nullable; + private Finitary finitary; + private Left left; + private First first; + private Follow follow; + + public Grammar(Symbol[] symbols, Prod[][] prods) throws Exception { + validate(symbols, prods); + this.symbols = symbols; + numSyms = symbols.length; + this.prods = prods; + numNTs = prods.length; + numTs = numSyms - numNTs; + calcDepends(); + comps = SCC.get(depends, revdeps, numNTs); + } + + private static void validate(Symbol[] symbol, Prod[][] prod) throws Exception { + if (symbol == null || symbol.length == 0) { + throw new Exception("No symbols specified"); + } + for (int i = 0; i < symbol.length; i++) { + if (symbol[i] == null) { + throw new Exception("Symbol " + i + " is null"); + } + } + int j = symbol.length; + if (prod == null || prod.length == 0) { + throw new Exception("No nonterminals specified"); + } + if (prod.length > j) { + throw new Exception("To many nonterminals specified"); + } + if (prod.length == j) { + throw new Exception("No terminals specified"); + } + for (int k = 0; k < prod.length; k++) { + if (prod[k] == null || prod[k].length == 0) { + throw new Exception("Nonterminal " + symbol[k] + " (number " + k + ") has no productions"); + } + for (int l = 0; l < prod[k].length; l++) { + int[] ai = prod[k][l].getRhs(); + if (ai == null) { + throw new Exception("Production " + l + " for symbol " + symbol[k] + " (number " + k + ") is null"); + } + for (int m : ai) { + if (m < 0 || m >= j - 1) { + throw new Exception("Out of range symbol " + m + " in production " + l + " for symbol " + + symbol[k] + " (number " + k + ")"); + } + } + } + } + } + + public int getNumSyms() { + return numSyms; + } + + public int getNumNTs() { + return numNTs; + } + + public int getNumTs() { + return numTs; + } + + public Symbol getSymbol(int i) { + return symbols[i]; + } + + Symbol getStart() { + return symbols[0]; + } + + Symbol getEnd() { + return symbols[numSyms - 1]; + } + + public Symbol getNonterminal(int i) { + return symbols[i]; + } + + public Symbol getTerminal(int i) { + return symbols[numNTs + i]; + } + + public boolean isNonterminal(int i) { + return 0 <= i && i < numNTs; + } + + public boolean isTerminal(int i) { + return numNTs <= i && i < numSyms; + } + + public int getNumProds() { + int i = 0; + for (Prod[] prod : prods) { + i += prod.length; + } + return i; + } + + public Prod[] getProds(int i) { + return prods[i]; + } + + int[][] getComponents() { + return comps; + } + + private void calcDepends() { + int[][] ai = new int[numNTs][]; + int[] ai1 = BitSet.make(numNTs); + depends = new int[numNTs][]; + for (int i = 0; i < numNTs; i++) { + ai[i] = BitSet.make(numNTs); + } + for (int j = 0; j < numNTs; j++) { + BitSet.clear(ai1); + for (int l = 0; l < prods[j].length; l++) { + int[] ai2 = prods[j][l].getRhs(); + for (int anAi2 : ai2) { + if (isNonterminal(anAi2)) { + BitSet.set(ai[anAi2], j); + BitSet.set(ai1, anAi2); + } + } + } + depends[j] = BitSet.members(ai1); + } + revdeps = new int[numNTs][]; + for (int k = 0; k < numNTs; k++) { + revdeps[k] = BitSet.members(ai[k]); + } + } + + public Nullable getNullable() { + if (nullable == null) { + nullable = new Nullable(this); + } + return nullable; + } + + public Finitary getFinitary() { + if (finitary == null) { + finitary = new Finitary(this); + } + return finitary; + } + + public Left getLeft() { + if (left == null) { + left = new Left(this); + } + return left; + } + + public First getFirst() { + if (first == null) { + first = new First(this, getNullable()); + } + return first; + } + + public Follow getFollow() { + if (follow == null) { + follow = new Follow(this, getNullable(), getFirst()); + } + return follow; + } + + public void display(Writer writer) throws IOException { + for (int i = 0; i < numNTs; i++) { + writer.write(symbols[i].getName() + "\n"); + String s = " = "; + for (int j = 0; j < prods[i].length; j++) { + int[] ai = prods[i][j].getRhs(); + writer.write(s); + writer.write(displaySymbols(ai, "/* empty */", " ") + "\n"); + s = " | "; + } + writer.write(" ;\n"); + } + } + + public String displaySymbols(int[] ai, String s, String s1) { + return displaySymbols(ai, 0, ai.length, s, s1); + } + + String displaySymbols(int[] ai, int i, int j, String s, String s1) { + if (ai == null || i >= j) { + return s; + } + StringBuilder sb = new StringBuilder(); + sb.append(symbols[ai[i]].getName()); + for (int k = i + 1; k < j; k++) { + sb.append(s1); + sb.append(symbols[ai[k]].getName()); + } + return sb.toString(); + } + + String displaySymbolSet(int[] ai, int i) { + StringBuilder sb = new StringBuilder(); + int j = 0; + for (Interator interator = BitSet.interator(ai, i); interator.hasNext(); + sb.append(symbols[interator.next()].getName())) { + if (j++ != 0) { + sb.append(", "); + } + } + return sb.toString(); + } + + /** + * + */ + public static class Prod { + + int[] rhs; + private int seqNo; + + public Prod(int[] ai, int i) { + rhs = ai; + seqNo = i; + } + + public int[] getRhs() { + return rhs; + } + + public int getSeqNo() { + return seqNo; + } + + public String getLabel() { + return null; + } + } + + /** + * + */ + public static class Symbol { + + String name; + + public Symbol(String s) { + name = s; + } + + public String getName() { + return name; + } + + public String toString() { + return name; + } + } +} diff --git a/src/main/java/org/xbib/jacc/grammar/LALRMachine.java b/src/main/java/org/xbib/jacc/grammar/LALRMachine.java new file mode 100644 index 0000000..c70d751 --- /dev/null +++ b/src/main/java/org/xbib/jacc/grammar/LALRMachine.java @@ -0,0 +1,218 @@ +package org.xbib.jacc.grammar; + +import org.xbib.jacc.util.BitSet; +import org.xbib.jacc.util.IntSet; +import org.xbib.jacc.util.SCC; + +import java.io.IOException; +import java.io.Writer; + +/** + * + */ +public class LALRMachine extends LookaheadMachine { + + private Nullable nullable; + private First first; + private int[][] predState; + private int numGotos; + private int[] stateFirstGoto; + private int[] gotoSource; + private int[] gotoTrans; + private int[][] gotoLA; + private int[][] gotoTargets; + private int[][][] laReds; + + public LALRMachine(Grammar grammar) { + super(grammar); + nullable = grammar.getNullable(); + first = grammar.getFirst(); + predState = SCC.invert(succState, numStates); + calcGotoLA(); + calcLookahead(); + } + + public int[] getLookaheadAt(int i, int j) { + return laReds[i][j]; + } + + private void calcGotoLA() { + stateFirstGoto = new int[numStates]; + numGotos = 0; + for (int i = 0; i < numStates; i++) { + stateFirstGoto[i] = numGotos; + numGotos += getGotosAt(i).length; + } + gotoSource = new int[numGotos]; + gotoTrans = new int[numGotos]; + int j = 0; + for (int k = 0; k < numStates; k++) { + int[] ai1 = getGotosAt(k); + for (int a : ai1) { + gotoSource[j] = k; + gotoTrans[j] = a; + j++; + } + } + gotoLA = new int[numGotos][]; + gotoTargets = new int[numGotos][]; + for (int l = 0; l < numGotos; l++) { + calcTargets(l); + } + int[][] ai = SCC.get(gotoTargets); + for (int[] ai2 : ai) { + for (boolean flag = true; flag; ) { + flag = false; + int k1 = 0; + while (k1 < ai2.length) { + int[] ai3 = gotoTargets[ai2[k1]]; + for (int a : ai3) { + if (BitSet.addTo(gotoLA[ai2[k1]], gotoLA[a])) { + flag = true; + } + } + k1++; + } + } + } + } + + private void calcTargets(int i) { + int j = gotoSource[i]; + int k = gotoTrans[i]; + int l = getEntry(k); + IntSet intset = getItemsAt(k); + int i1 = intset.size(); + int[] ai = BitSet.make(numTs); + IntSet intset1 = IntSet.empty(); + for (int j1 = 0; j1 < i1; j1++) { + LR0Items.Item item = items.getItem(intset.at(j1)); + int k1 = item.getLhs(); + int l1 = item.getPos(); + if (k1 >= 0) { + int[] ai1 = item.getProd().getRhs(); + if (l1 > 0 && ai1[--l1] == l && calcFirsts(ai, item).canReduce()) { + findTargets(intset1, j, k1, ai1, l1); + } + continue; + } + if (l1 > 0) { + BitSet.set(ai, numTs - 1); + } + } + gotoLA[i] = ai; + gotoTargets[i] = intset1.toArray(); + } + + private LR0Items.Item calcFirsts(int[] ai, LR0Items.Item item) { + do { + if (!item.canGoto()) { + break; + } + int i = item.getNextSym(); + if (grammar.isTerminal(i)) { + BitSet.addTo(ai, i - numNTs); + break; + } + BitSet.union(ai, first.at(i)); + if (!nullable.at(i)) { + break; + } + item = items.getItem(item.getNextItem()); + } while (true); + if (item.canAccept()) { + BitSet.set(ai, numTs - 1); + } + return item; + } + + private void findTargets(IntSet intset, int i, int j, int[] ai, int k) { + if (k == 0) { + int[] ai1 = getGotosAt(i); + int i1 = 0; + do { + if (i1 >= ai1.length) { + break; + } + if (getEntry(ai1[i1]) == j) { + intset.add(stateFirstGoto[i] + i1); + break; + } + i1++; + } while (true); + } else if (entry[i] == ai[--k]) { + for (int l = 0; l < predState[i].length; l++) { + findTargets(intset, predState[i][l], j, ai, k); + } + } + } + + private void calcLookahead() { + laReds = new int[numStates][][]; + for (int i = 0; i < numStates; i++) { + int[] ai = getReducesAt(i); + IntSet intset = getItemsAt(i); + laReds[i] = new int[ai.length][]; + for (int j = 0; j < ai.length; j++) { + LR0Items.Item item = items.getItem(intset.at(ai[j])); + int k = item.getLhs(); + int[] ai1 = item.getProd().getRhs(); + int[] ai2 = BitSet.make(numTs); + lookBack(ai2, i, k, ai1, ai1.length); + laReds[i][j] = ai2; + } + } + } + + private void lookBack(int[] ai, int i, int j, int[] ai1, int k) { + if (k == 0) { + int[] ai2 = getGotosAt(i); + for (int i1 = 0; i1 < ai2.length; i1++) { + if (getEntry(ai2[i1]) == j) { + BitSet.union(ai, gotoLA[stateFirstGoto[i] + i1]); + return; + } + } + } else if (entry[i] == ai1[--k]) { + for (int l = 0; l < predState[i].length; l++) { + lookBack(ai, predState[i][l], j, ai1, k); + } + } + } + + public void display(Writer writer) throws IOException { + super.display(writer); + for (int i = 0; i < numGotos; i++) { + writer.write("Goto #" + i + ", in state " + gotoSource[i] + " on symbol " + + grammar.getSymbol(getEntry(gotoTrans[i])) + " to state " + gotoTrans[i] + "\n"); + writer.write(" Lookahead: {"); + writer.write(grammar.displaySymbolSet(gotoLA[i], numNTs)); + writer.write("}\n"); + writer.write(" Targets : {"); + for (int k = 0; k < gotoTargets[i].length; k++) { + if (k > 0) { + writer.write(", "); + } + writer.write(gotoTargets[i][k]); + } + writer.write("}\n"); + } + for (int j = 0; j < numStates; j++) { + int[] ai = getReducesAt(j); + if (ai.length <= 0) { + continue; + } + writer.write("State " + j + ":\n"); + IntSet intset = getItemsAt(j); + for (int l = 0; l < ai.length; l++) { + LR0Items.Item item = items.getItem(intset.at(ai[l])); + writer.write(" Item : "); + item.display(writer); + writer.write("\n"); + writer.write(" Lookahead: {"); + writer.write(grammar.displaySymbolSet(laReds[j][l], numNTs)); + writer.write("}\n"); + } + } + } +} diff --git a/src/main/java/org/xbib/jacc/grammar/LR0Items.java b/src/main/java/org/xbib/jacc/grammar/LR0Items.java new file mode 100644 index 0000000..0b2f9e7 --- /dev/null +++ b/src/main/java/org/xbib/jacc/grammar/LR0Items.java @@ -0,0 +1,158 @@ +package org.xbib.jacc.grammar; + +import java.io.IOException; +import java.io.Writer; + +/** + * + */ +public class LR0Items { + + private Grammar grammar; + private int numItems; + private Item[] items; + private int[][] firstKernel; + + LR0Items(Grammar grammar) { + this.grammar = grammar; + int i = grammar.getNumNTs(); + numItems = 2; + firstKernel = new int[i][]; + for (int j = 0; j < i; j++) { + Grammar.Prod[] aprod = grammar.getProds(j); + firstKernel[j] = new int[aprod.length]; + for (int l = 0; l < aprod.length; l++) { + int j1 = aprod[l].getRhs().length; + firstKernel[j][l] = numItems; + numItems += j1 != 0 ? j1 : 1; + } + + } + items = new Item[numItems]; + numItems = 0; + new Item(-1, 0, 0); + new Item(-1, 0, 1); + for (int k = 0; k < i; k++) { + Grammar.Prod[] aprod1 = grammar.getProds(k); + for (int i1 = 0; i1 < aprod1.length; i1++) { + int[] ai = aprod1[i1].getRhs(); + for (int k1 = 1; k1 < ai.length; k1++) { + new Item(k, i1, k1); + } + new Item(k, i1, ai.length); + } + } + } + + public Item getItem(int i) { + return items[i]; + } + + int getStartItem() { + return 0; + } + + int getEndItem() { + return 1; + } + + int getFirstKernel(int i, int j) { + return firstKernel[i][j]; + } + + /** + * + */ + public class Item { + private int itemNo; + private int lhs; + private int prodNo; + private int pos; + + private Item(int i, int j, int k) { + itemNo = numItems; + lhs = i; + prodNo = j; + pos = k; + items[numItems++] = this; + } + + int getLhs() { + return lhs; + } + + int getProdNo() { + return prodNo; + } + + public int getSeqNo() { + return getProd().getSeqNo(); + } + + public Grammar.Prod getProd() { + return grammar.getProds(lhs)[prodNo]; + } + + int getPos() { + return pos; + } + + boolean canGoto() { + if (lhs < 0) { + return pos == 0; + } else { + return pos != getProd().getRhs().length; + } + } + + boolean canReduce() { + return lhs >= 0 && pos == getProd().getRhs().length; + } + + boolean canAccept() { + return lhs < 0 && pos == 1; + } + + int getNextItem() { + if (lhs >= 0) { + return itemNo + 1; + } else { + return 1; + } + } + + int getNextSym() { + if (lhs >= 0) { + return grammar.getProds(lhs)[prodNo].getRhs()[pos]; + } else { + return 0; + } + } + + public void display(Writer writer) throws IOException { + if (lhs < 0) { + if (pos == 0) { + writer.write("$accept : _" + grammar.getStart() + " " + grammar.getEnd()); + } else { + writer.write("$accept : " + grammar.getStart() + "_" + grammar.getEnd()); + } + return; + } + writer.write(grammar.getSymbol(lhs).toString()); + writer.write(" : "); + Grammar.Prod prod = grammar.getProds(lhs)[prodNo]; + int[] ai = prod.getRhs(); + writer.write(grammar.displaySymbols(ai, 0, pos, "", " ")); + writer.write("_"); + if (pos < ai.length) { + writer.write(grammar.displaySymbols(ai, pos, ai.length, "", " ")); + } + String s = prod.getLabel(); + if (s != null) { + writer.write(" ("); + writer.write(s); + writer.write(')'); + } + } + } +} diff --git a/src/main/java/org/xbib/jacc/grammar/LR0Machine.java b/src/main/java/org/xbib/jacc/grammar/LR0Machine.java new file mode 100644 index 0000000..e664295 --- /dev/null +++ b/src/main/java/org/xbib/jacc/grammar/LR0Machine.java @@ -0,0 +1,34 @@ +package org.xbib.jacc.grammar; + +import org.xbib.jacc.util.BitSet; + +import java.io.IOException; +import java.io.Writer; + +/** + * + */ +public class LR0Machine extends LookaheadMachine { + + private int allTokens[]; + + public LR0Machine(Grammar grammar) { + super(grammar); + int i = grammar.getNumTs(); + allTokens = BitSet.make(i); + for (int j = 0; j < i; j++) { + BitSet.set(allTokens, j); + } + } + + public int[] getLookaheadAt(int i, int j) { + return allTokens; + } + + public void display(Writer writer) throws IOException { + super.display(writer); + writer.write("Lookahead set is {"); + writer.write(grammar.displaySymbolSet(allTokens, numNTs)); + writer.write("}\n"); + } +} diff --git a/src/main/java/org/xbib/jacc/grammar/Left.java b/src/main/java/org/xbib/jacc/grammar/Left.java new file mode 100644 index 0000000..aa378f0 --- /dev/null +++ b/src/main/java/org/xbib/jacc/grammar/Left.java @@ -0,0 +1,52 @@ +package org.xbib.jacc.grammar; + +import org.xbib.jacc.util.BitSet; + +import java.io.IOException; +import java.io.Writer; + +/** + * + */ +final class Left extends Analysis { + + private Grammar grammar; + private int[][] left; + + Left(Grammar grammar1) { + super(grammar1.getComponents()); + grammar = grammar1; + int numNTs = grammar1.getNumNTs(); + left = new int[numNTs][]; + for (int i = 0; i < numNTs; i++) { + left[i] = BitSet.make(numNTs); + BitSet.set(left[i], i); + } + bottomUp(); + } + + protected boolean analyze(int i) { + boolean flag = false; + Grammar.Prod aprod[] = grammar.getProds(i); + for (Grammar.Prod anAprod : aprod) { + int ai[] = anAprod.getRhs(); + if (ai.length > 0 && grammar.isNonterminal(ai[0]) && BitSet.addTo(left[i], left[ai[0]])) { + flag = true; + } + } + return flag; + } + + public int[] at(int i) { + return left[i]; + } + + public void display(Writer writer) throws IOException { + writer.write("Left nonterminal sets:\n"); + for (int i = 0; i < left.length; i++) { + writer.write(" Left(" + grammar.getSymbol(i) + "): {"); + writer.write(grammar.displaySymbolSet(left[i], 0)); + writer.write("}\n"); + } + } +} diff --git a/src/main/java/org/xbib/jacc/grammar/LookaheadMachine.java b/src/main/java/org/xbib/jacc/grammar/LookaheadMachine.java new file mode 100644 index 0000000..cfb4d6b --- /dev/null +++ b/src/main/java/org/xbib/jacc/grammar/LookaheadMachine.java @@ -0,0 +1,13 @@ +package org.xbib.jacc.grammar; + +/** + * + */ +public abstract class LookaheadMachine extends Machine { + + LookaheadMachine(Grammar grammar) { + super(grammar); + } + + public abstract int[] getLookaheadAt(int i, int j); +} diff --git a/src/main/java/org/xbib/jacc/grammar/Machine.java b/src/main/java/org/xbib/jacc/grammar/Machine.java new file mode 100644 index 0000000..adf1b83 --- /dev/null +++ b/src/main/java/org/xbib/jacc/grammar/Machine.java @@ -0,0 +1,269 @@ +package org.xbib.jacc.grammar; + +import org.xbib.jacc.util.BitSet; +import org.xbib.jacc.util.IntSet; +import org.xbib.jacc.util.Interator; + +import java.io.IOException; +import java.io.Writer; + +/** + * + */ +public class Machine { + + private final IntSet acceptItems = IntSet.singleton(-1); + protected Grammar grammar; + protected int numNTs; + protected int numTs; + protected Left left; + LR0Items items; + int numStates; + int[] entry; + int[][] succState; + private int numSyms; + private IntSet[] stateSets; + private IntSet[] nullReds; + private int[][] gotos; + private int[][] shifts; + private int[][] reduceOffsets; + + Machine(Grammar grammar) { + this.grammar = grammar; + numSyms = grammar.getNumSyms(); + numNTs = grammar.getNumNTs(); + numTs = grammar.getNumTs(); + left = grammar.getLeft(); + items = new LR0Items(grammar); + calcLR0states(); + calcGotosShifts(); + calcReduceOffsets(); + } + + public Grammar getGrammar() { + return grammar; + } + + public int getNumStates() { + return numStates; + } + + public LR0Items getItems() { + return items; + } + + public LR0Items.Item reduceItem(int i, int j) { + return items.getItem(stateSets[i].at(j)); + } + + public int getEntry(int i) { + return i >= 0 ? entry[i] : numSyms - 1; + } + + public IntSet getItemsAt(int i) { + return stateSets[i]; + } + + public int[] getGotosAt(int i) { + return gotos[i]; + } + + public int[] getShiftsAt(int i) { + return shifts[i]; + } + + int[] getReducesAt(int i) { + return reduceOffsets[i]; + } + + private void calcLR0states() { + stateSets = new IntSet[16]; + succState = new int[16][]; + entry = new int[16]; + nullReds = new IntSet[16]; + stateSets[0] = IntSet.singleton(items.getStartItem()); + numStates = 1; + IntSet[] aintset = new IntSet[numSyms]; + int i = 0; + int[] ai = BitSet.make(numNTs); + for (int j = 0; j < numStates; j++) { + IntSet intset = stateSets[j]; + BitSet.clear(ai); + Interator interator = intset.interator(); + do { + if (!interator.hasNext()) { + break; + } + LR0Items.Item item = items.getItem(interator.next()); + if (item.canGoto()) { + int k = item.getNextSym(); + int i1 = item.getNextItem(); + if (grammar.isNonterminal(k)) { + BitSet.addTo(ai, left.at(k)); + } + if (addValue(aintset, k, i1)) { + i++; + } + } + } while (true); + if (!BitSet.isEmpty(ai)) { + for (Interator interator1 = BitSet.interator(ai, 0); interator1.hasNext(); ) { + int l = interator1.next(); + Grammar.Prod[] aprod = grammar.getProds(l); + int k1 = 0; + while (k1 < aprod.length) { + int[] ai3 = aprod[k1].getRhs(); + int i2 = items.getFirstKernel(l, k1); + if (ai3.length != 0) { + if (addValue(aintset, ai3[0], i2)) { + i++; + } + } else { + addValue(nullReds, j, i2); + } + k1++; + } + } + } + int[] ai1 = new int[i]; + int j1 = 0; + for (int l1 = 0; j1 < i; l1++) { + if (aintset[l1] != null) { + ai1[j1] = addState(l1, aintset[l1]); + aintset[l1] = null; + j1++; + } + } + i = 0; + succState[j] = ai1; + } + mergeNullReds(); + } + + private boolean addValue(IntSet[] aintset, int i, int j) { + if (aintset[i] == null) { + aintset[i] = IntSet.singleton(j); + return true; + } else { + aintset[i].add(j); + return false; + } + } + + private int addState(int i, IntSet intset) { + for (int j = 0; j < numStates; j++) { + if (stateSets[j].equals(intset)) { + return j; + } + } + if (acceptItems.equals(intset)) { + return -1; + } + if (numStates >= stateSets.length) { + int k = 2 * stateSets.length; + IntSet[] aintset = new IntSet[k]; + int[][] ai = new int[k][]; + IntSet[] aintset1 = new IntSet[k]; + int[] ai1 = new int[k]; + for (int l = 0; l < numStates; l++) { + aintset[l] = stateSets[l]; + ai[l] = succState[l]; + ai1[l] = entry[l]; + aintset1[l] = nullReds[l]; + } + stateSets = aintset; + succState = ai; + entry = ai1; + nullReds = aintset1; + } + stateSets[numStates] = intset; + entry[numStates] = i; + return numStates++; + } + + private void mergeNullReds() { + for (int i = 0; i < numStates; i++) { + if (nullReds[i] == null) { + continue; + } + Interator interator = nullReds[i].interator(); + while (interator.hasNext()) { + stateSets[i].add(interator.next()); + } + nullReds[i] = null; + } + } + + private void calcGotosShifts() { + gotos = new int[numStates][]; + shifts = new int[numStates][]; + for (int i = 0; i < numStates; i++) { + int j = 0; + int k = 0; + for (int l = 0; l < succState[i].length; l++) { + int j1 = succState[i][l]; + if (grammar.isTerminal(entry[j1])) { + k++; + } else { + j++; + } + } + if (stateSets[i].contains(items.getEndItem())) { + k++; + } + gotos[i] = new int[j]; + shifts[i] = new int[k]; + for (int i1 = succState[i].length; 0 < i1--; ) { + int k1 = succState[i][i1]; + if (grammar.isTerminal(entry[k1])) { + shifts[i][--k] = k1; + } else { + gotos[i][--j] = k1; + } + } + if (k > 0) { + shifts[i][0] = -1; + } + } + } + + private void calcReduceOffsets() { + reduceOffsets = new int[numStates][]; + for (int i = 0; i < numStates; i++) { + int j = 0; + IntSet intset = stateSets[i]; + int k = intset.size(); + for (int l = 0; l < k; l++) { + if (items.getItem(intset.at(l)).canReduce()) { + j++; + } + } + reduceOffsets[i] = new int[j]; + int i1 = 0; + for (int j1 = 0; j1 < k; j1++) { + if (items.getItem(intset.at(j1)).canReduce()) { + reduceOffsets[i][i1++] = j1; + } + } + } + } + + public void display(Writer writer) throws IOException { + for (int i = 0; i < numStates; i++) { + writer.write("state " + i + "\n"); + for (Interator interator = stateSets[i].interator(); interator.hasNext(); writer.write("\n")) { + writer.write("\t"); + items.getItem(interator.next()).display(writer); + } + writer.write("\n"); + if (succState[i].length <= 0) { + continue; + } + for (int j = 0; j < succState[i].length; j++) { + int k = succState[i][j]; + writer.write("\t" + grammar.getSymbol(entry[k]) + " goto " + succState[i][j] + "\n"); + } + writer.write("\n"); + } + } +} diff --git a/src/main/java/org/xbib/jacc/grammar/Nullable.java b/src/main/java/org/xbib/jacc/grammar/Nullable.java new file mode 100644 index 0000000..7296c7b --- /dev/null +++ b/src/main/java/org/xbib/jacc/grammar/Nullable.java @@ -0,0 +1,77 @@ +package org.xbib.jacc.grammar; + +import java.io.IOException; +import java.io.Writer; + +/** + * + */ +public class Nullable extends Analysis { + + private boolean nullable[]; + private boolean consider[]; + private Grammar grammar; + private int numNTs; + + Nullable(Grammar grammar) { + super(grammar.getComponents()); + this.grammar = grammar; + numNTs = grammar.getNumNTs(); + nullable = new boolean[numNTs]; + consider = new boolean[numNTs]; + for (int i = 0; i < numNTs; i++) { + nullable[i] = false; + consider[i] = true; + } + bottomUp(); + } + + protected boolean analyze(int i) { + boolean flag = false; + if (consider[i]) { + int j = 0; + Grammar.Prod aprod[] = grammar.getProds(i); + for (Grammar.Prod anAprod : aprod) { + int ai[] = anAprod.getRhs(); + int l; + l = 0; + while (l < ai.length && at(ai[l])) { + l++; + } + if (l >= ai.length) { + nullable[i] = true; + consider[i] = false; + flag = true; + break; + } + if (grammar.isTerminal(ai[l]) || grammar.isNonterminal(ai[l]) && !consider[ai[l]]) { + j++; + } + } + if (j == aprod.length) { + consider[i] = false; + } + } + return flag; + } + + public boolean at(int i) { + return grammar.isNonterminal(i) && nullable[i]; + } + + public void display(Writer writer) throws IOException { + writer.write("Nullable = {"); + int i = 0; + for (int j = 0; j < numNTs; j++) { + if (!at(j)) { + continue; + } + if (i > 0) { + writer.write(", "); + } + writer.write(grammar.getSymbol(j).getName()); + i++; + } + writer.write("}\n"); + } +} diff --git a/src/main/java/org/xbib/jacc/grammar/Parser.java b/src/main/java/org/xbib/jacc/grammar/Parser.java new file mode 100644 index 0000000..b564f90 --- /dev/null +++ b/src/main/java/org/xbib/jacc/grammar/Parser.java @@ -0,0 +1,197 @@ +package org.xbib.jacc.grammar; + +import java.io.IOException; +import java.io.Writer; + +/** + * + */ +public class Parser { + + private static final int ACCEPT = 0; + private static final int ERROR = 1; + private static final int SHIFT = 2; + private static final int GOTO = 3; + private static final int REDUCE = 4; + private Tables tables; + private int input[]; + private Machine machine; + private Grammar grammar; + private int position; + private int currSymbol; + private int reducedNT; + private Stack stack; + private int state; + + public Parser(Tables tables, int ai[]) { + position = 0; + currSymbol = -1; + reducedNT = -1; + stack = new Stack(); + state = ACCEPT; + this.tables = tables; + input = ai; + machine = tables.getMachine(); + grammar = machine.getGrammar(); + } + + public int getState() { + return state; + } + + public int getNextSymbol() { + return reducedNT < 0 ? currSymbol : reducedNT; + } + + public int step() { + if (state < 0) { + return ACCEPT; + } + if (reducedNT >= 0) { + shift(reducedNT); + if (!gotoState(reducedNT)) { + return ERROR; + } else { + reducedNT = -1; + return GOTO; + } + } + if (currSymbol < 0) { + currSymbol = position < input.length ? input[position++] : grammar.getNumSyms() - 1; + } + if (grammar.isNonterminal(currSymbol)) { + shift(currSymbol); + if (!gotoState(currSymbol)) { + return ERROR; + } else { + currSymbol = -1; + return GOTO; + } + } + byte[] b = tables.getActionAt(state); + int[] ai = tables.getArgAt(state); + int i = currSymbol - grammar.getNumNTs(); + switch (b[i]) { + case 1: + if (ai[i] < 0) { + return ACCEPT; + } else { + shift(currSymbol); + currSymbol = -1; + state = ai[i]; + return SHIFT; + } + case 2: + reduce(ai[i]); + return REDUCE; + } + return ERROR; + } + + private void shift(int i) { + stack = stack.push(state, i); + } + + private void reduce(int i) { + LR0Items.Item item = machine.reduceItem(state, i); + int j = item.getProd().getRhs().length; + if (j > 0) { + for (; j > 1; j--) { + stack = stack.pop(); + } + state = stack.getState(); + stack = stack.pop(); + } + reducedNT = item.getLhs(); + } + + private boolean gotoState(int i) { + int ai[] = machine.getGotosAt(state); + for (int anAi : ai) { + if (i == machine.getEntry(anAi)) { + state = anAi; + return true; + } + } + return false; + } + + public void display(Writer writer, boolean flag) throws IOException { + stack.display(writer, grammar, flag); + if (flag) { + writer.write(state); + writer.write(" "); + } + writer.write("_ "); + if (reducedNT >= 0) { + writer.write(grammar.getSymbol(reducedNT).toString()); + writer.write(" "); + } + if (currSymbol >= 0) { + writer.write(grammar.getSymbol(currSymbol).toString()); + if (position < input.length) { + writer.write(" ..."); + } + } else { + if (position < input.length) { + writer.write(grammar.getSymbol(input[position]).toString()); + writer.write(" ..."); + } + } + writer.write("\n"); + } + + /** + * + */ + private static class Stack { + + private int state; + private int symbol; + private Stack up; + private Stack down; + + Stack() { + this(null); + } + + private Stack(Stack stack) { + down = stack; + up = null; + } + + int getState() { + return state; + } + + public int getSymbol() { + return symbol; + } + + Stack pop() { + return down; + } + + Stack push(int i, int j) { + Stack stack1 = up; + if (stack1 == null) { + stack1 = up = new Stack(this); + } + stack1.state = i; + stack1.symbol = j; + return stack1; + } + + public void display(Writer writer, Grammar grammar1, boolean flag) throws IOException { + if (down != null) { + down.display(writer, grammar1, flag); + if (flag) { + writer.write(state); + writer.write(" "); + } + writer.write(grammar1.getSymbol(symbol).toString()); + writer.write(" "); + } + } + } +} diff --git a/src/main/java/org/xbib/jacc/grammar/Resolver.java b/src/main/java/org/xbib/jacc/grammar/Resolver.java new file mode 100644 index 0000000..e5e407a --- /dev/null +++ b/src/main/java/org/xbib/jacc/grammar/Resolver.java @@ -0,0 +1,11 @@ +package org.xbib.jacc.grammar; + +/** + * + */ +public interface Resolver { + + void srResolve(Tables tables, int i, int j, int k); + + void rrResolve(Tables tables, int i, int j, int k); +} diff --git a/src/main/java/org/xbib/jacc/grammar/SLRMachine.java b/src/main/java/org/xbib/jacc/grammar/SLRMachine.java new file mode 100644 index 0000000..3c2bff7 --- /dev/null +++ b/src/main/java/org/xbib/jacc/grammar/SLRMachine.java @@ -0,0 +1,58 @@ +package org.xbib.jacc.grammar; + +import org.xbib.jacc.util.IntSet; + +import java.io.IOException; +import java.io.Writer; + +/** + * + */ +public class SLRMachine extends LookaheadMachine { + + private Follow follow; + private int[][][] laReds; + + public SLRMachine(Grammar grammar) { + super(grammar); + follow = grammar.getFollow(); + calcLookahead(); + } + + public int[] getLookaheadAt(int i, int j) { + return laReds[i][j]; + } + + private void calcLookahead() { + laReds = new int[numStates][][]; + for (int i = 0; i < numStates; i++) { + IntSet intset = getItemsAt(i); + int[] ai = getReducesAt(i); + laReds[i] = new int[ai.length][]; + for (int j = 0; j < ai.length; j++) { + int k = items.getItem(intset.at(ai[j])).getLhs(); + laReds[i][j] = follow.at(k); + } + } + } + + public void display(Writer writer) throws IOException { + super.display(writer); + for (int i = 0; i < numStates; i++) { + IntSet intset = getItemsAt(i); + int[] ai = getReducesAt(i); + if (ai.length <= 0) { + continue; + } + writer.write("In state " + i + ":"); + for (int j = 0; j < ai.length; j++) { + writer.write(" Item: "); + items.getItem(intset.at(ai[j])).display(writer); + writer.write("\n"); + writer.write(" Lookahead: {"); + writer.write(grammar.displaySymbolSet(laReds[i][j], numNTs)); + writer.write("}\n"); + } + } + } +} diff --git a/src/main/java/org/xbib/jacc/grammar/Tables.java b/src/main/java/org/xbib/jacc/grammar/Tables.java new file mode 100644 index 0000000..0d4ee70 --- /dev/null +++ b/src/main/java/org/xbib/jacc/grammar/Tables.java @@ -0,0 +1,118 @@ +package org.xbib.jacc.grammar; + +import org.xbib.jacc.util.BitSet; +import org.xbib.jacc.util.IntSet; +import org.xbib.jacc.util.Interator; + +/** + * + */ +public class Tables { + + private static final byte NONE = 0; + private static final byte SHIFT = 1; + private static final byte REDUCE = 2; + protected LookaheadMachine machine; + protected int numNTs; + protected int numTs; + protected byte[][] action; + protected int[][] arg; + private Resolver resolver; + private boolean[][] prodUsed; + private int prodUnused; + + public Tables(LookaheadMachine lookaheadmachine, Resolver resolver) { + machine = lookaheadmachine; + this.resolver = resolver; + Grammar grammar = lookaheadmachine.getGrammar(); + numNTs = grammar.getNumNTs(); + numTs = grammar.getNumTs(); + int i = lookaheadmachine.getNumStates(); + action = new byte[i][]; + arg = new int[i][]; + prodUsed = new boolean[numNTs][]; + prodUnused = 0; + for (int j = 0; j < numNTs; j++) { + prodUsed[j] = new boolean[grammar.getProds(j).length]; + prodUnused += prodUsed[j].length; + } + for (int k = 0; k < i; k++) { + fillTablesAt(k); + } + } + + public LookaheadMachine getMachine() { + return machine; + } + + public byte[] getActionAt(int i) { + return action[i]; + } + + public int[] getArgAt(int i) { + return arg[i]; + } + + public int getProdUnused() { + return prodUnused; + } + + public boolean[] getProdsUsedAt(int i) { + return prodUsed[i]; + } + + private void setShift(int i, int j, int k) { + action[i][j] = SHIFT; + arg[i][j] = k; + } + + public void setReduce(int i, int j, int k) { + action[i][j] = REDUCE; + arg[i][j] = k; + } + + private void fillTablesAt(int i) { + int ai1[]; + action[i] = new byte[numTs]; + arg[i] = new int[numTs]; + int ai[] = machine.getShiftsAt(i); + ai1 = machine.getReducesAt(i); + for (int anAi : ai) { + setShift(i, machine.getEntry(anAi) - numNTs, anAi); + } + for (int k = 0; k < ai1.length; k++) { + for (Interator interator = BitSet.interator(machine.getLookaheadAt(i, k), 0); + interator.hasNext(); ) { + int l = interator.next(); + switch (action[i][l]) { + case NONE: + setReduce(i, l, ai1[k]); + break; + case SHIFT: + resolver.srResolve(this, i, l, ai1[k]); + break; + case REDUCE: + resolver.rrResolve(this, i, l, ai1[k]); + break; + default: + break; + } + } + } + LR0Items lr0items = machine.getItems(); + IntSet intset = machine.getItemsAt(i); + for (int anAi1 : ai1) { + for (int j1 = 0; j1 < numTs; j1++) { + if (action[i][j1] == REDUCE && arg[i][j1] == anAi1) { + LR0Items.Item item = lr0items.getItem(intset.at(anAi1)); + int k1 = item.getLhs(); + int l1 = item.getProdNo(); + if (!prodUsed[k1][l1]) { + prodUsed[k1][l1] = true; + prodUnused--; + } + } + } + } + } +} diff --git a/src/main/java/org/xbib/jacc/grammar/package-info.java b/src/main/java/org/xbib/jacc/grammar/package-info.java new file mode 100644 index 0000000..35f0b2e --- /dev/null +++ b/src/main/java/org/xbib/jacc/grammar/package-info.java @@ -0,0 +1,4 @@ +/** + * Classes for grammar construction. + */ +package org.xbib.jacc.grammar; diff --git a/src/main/java/org/xbib/jacc/package-info.java b/src/main/java/org/xbib/jacc/package-info.java new file mode 100644 index 0000000..1d2bccb --- /dev/null +++ b/src/main/java/org/xbib/jacc/package-info.java @@ -0,0 +1,4 @@ +/** + * Classes for compiler generation. + */ +package org.xbib.jacc; diff --git a/src/main/java/org/xbib/jacc/util/BitSet.java b/src/main/java/org/xbib/jacc/util/BitSet.java new file mode 100644 index 0000000..86078a9 --- /dev/null +++ b/src/main/java/org/xbib/jacc/util/BitSet.java @@ -0,0 +1,169 @@ +package org.xbib.jacc.util; + +/** + * + */ +public class BitSet { + private static final int LOG_BITS_PER_WORD = 5; + private static final int BITS_PER_WORD = 32; + + private BitSet() { + } + + public static int[] make(int i) { + return new int[(i + 32) - 1 >> 5]; + } + + public static void clear(int[] ai) { + for (int i = 0; i < ai.length; i++) { + ai[i] = 0; + } + } + + public static boolean isEmpty(int[] ai) { + for (int anAi : ai) { + if (anAi != 0) { + return false; + } + } + return true; + } + + public static void union(int[] ai, int[] ai1) { + for (int i = 0; i < ai.length; i++) { + ai[i] |= ai1[i]; + } + } + + public static boolean addTo(int[] ai, int[] ai1) { + if (ai.length < ai1.length) { + throw new Error("bitset arguments do not match"); + } + int i = 0; + boolean flag = false; + for (; i < ai1.length; i++) { + if (ai1[i] == 0) { + continue; + } + int j = ai[i] | ai1[i]; + if (j != ai[i]) { + flag = true; + } + ai[i] = j; + } + return flag; + } + + public static boolean addTo(int[] ai, int i) { + int j = 1 << (i & 0x1f); + int k = i >> LOG_BITS_PER_WORD; + int l = ai[k] | j; + if (l != ai[k]) { + ai[k] = l; + return true; + } else { + return false; + } + } + + public static void set(int[] ai, int i) { + int j = 1 << (i & 0x1f); + int k = i >> LOG_BITS_PER_WORD; + ai[k] |= j; + } + + public static boolean get(int[] ai, int i) { + int j = 1 << (i & 0x1f); + int k = i >> LOG_BITS_PER_WORD; + return (ai[k] & j) != 0; + } + + public static int[] members(int[] ai) { + int i = 0; + label0: + for (int anAi : ai) { + if (anAi == 0) { + continue; + } + int k = anAi; + int i1 = 0; + do { + if (i1 >= BITS_PER_WORD || k == 0) { + continue label0; + } + if ((k & 1) != 0) { + i++; + } + k >>= 1; + i1++; + } while (true); + } + int[] ai1 = new int[i]; + int l = 0; + label1: + for (int j1 = 0; j1 < ai.length && l < i; j1++) { + if (ai[j1] == 0) { + continue; + } + int k1 = j1 << LOG_BITS_PER_WORD; + int l1 = ai[j1]; + int i2 = 0; + do { + if (i2 >= BITS_PER_WORD || l1 == 0) { + continue label1; + } + if ((l1 & 1) != 0) { + ai1[l++] = k1 + i2; + } + l1 >>= 1; + i2++; + } while (true); + } + return ai1; + } + + public static Interator interator(int[] ai, int i) { + return new BitSetInterator(ai, i); + } + + private static class BitSetInterator extends Interator { + + int[] set; + int pos; + int mask; + int num; + int bitCount; + + BitSetInterator(int[] ai, int i) { + set = ai; + num = i; + pos = 0; + mask = 1; + bitCount = 0; + } + + private void advance() { + num++; + if (++bitCount == 32) { + pos++; + bitCount = 0; + mask = 1; + } else { + mask <<= 1; + } + } + + public int next() { + int i = num; + advance(); + return i; + } + + public boolean hasNext() { + while (pos < set.length && (set[pos] & mask) == 0) { + advance(); + } + return pos < set.length; + } + } +} diff --git a/src/main/java/org/xbib/jacc/util/ElemInterator.java b/src/main/java/org/xbib/jacc/util/ElemInterator.java new file mode 100644 index 0000000..719469a --- /dev/null +++ b/src/main/java/org/xbib/jacc/util/ElemInterator.java @@ -0,0 +1,29 @@ +package org.xbib.jacc.util; + +/** + * + */ +class ElemInterator extends Interator { + + private int count; + private int limit; + private int[] a; + + ElemInterator(int[] ai, int i, int j) { + a = ai; + count = i; + limit = j; + } + + ElemInterator(int[] ai) { + this(ai, 0, ai.length); + } + + public int next() { + return a[count++]; + } + + public boolean hasNext() { + return count < limit; + } +} diff --git a/src/main/java/org/xbib/jacc/util/IntSet.java b/src/main/java/org/xbib/jacc/util/IntSet.java new file mode 100644 index 0000000..a8adc27 --- /dev/null +++ b/src/main/java/org/xbib/jacc/util/IntSet.java @@ -0,0 +1,127 @@ +package org.xbib.jacc.util; + +import java.io.IOException; +import java.io.Writer; +import java.util.Arrays; + +/** + * + */ +public class IntSet { + + private int[] elems; + private int used; + + private IntSet() { + elems = new int[1]; + used = 0; + } + + public static IntSet empty() { + return new IntSet(); + } + + public static IntSet singleton(int i) { + IntSet intset = new IntSet(); + intset.add(i); + return intset; + } + + public int size() { + return used; + } + + public int at(int i) { + return elems[i]; + } + + public int[] toArray() { + int[] ai = new int[used]; + System.arraycopy(elems, 0, ai, 0, used); + return ai; + } + + public boolean contains(int i) { + int j = 0; + for (int k = used; j < k; ) { + int l = j + ((k - j) / 2); + int i1 = elems[l]; + if (i == i1) { + return true; + } + if (i < i1) { + k = l; + } else { + j = l + 1; + } + } + return false; + } + + public void add(int i) { + int j = 0; + for (int k = used; j < k; ) { + int l = j + ((k - j) / 2); + int j1 = elems[l]; + if (i < j1) { + k = l; + } else { + if (i == j1) { + return; + } + j = l + 1; + } + } + if (used >= elems.length) { + int[] ai = new int[elems.length * 2]; + System.arraycopy(elems, 0, ai, 0, j); + ai[j] = i; + System.arraycopy(elems, j, ai, j + 1, used - j); + elems = ai; + } else { + System.arraycopy(elems, j, elems, j + 1, used - j); + elems[j] = i; + } + used++; + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof IntSet)) { + return false; + } + IntSet intset = (IntSet) o; + if (used == intset.used) { + for (int i = 0; i < used; i++) { + if (elems[i] != intset.elems[i]) { + return false; + } + } + return true; + } else { + return false; + } + } + + @Override + public int hashCode() { + return Arrays.hashCode(elems); + } + + public Interator interator() { + return new ElemInterator(elems, 0, used); + } + + public void display(Writer writer) throws IOException { + Interator interator1 = interator(); + writer.write("{"); + for (int i = 0; interator1.hasNext(); i++) { + if (i != 0) { + writer.write(", "); + } + writer.write(interator1.next()); + } + writer.write("}"); + writer.write(": used = " + used + ", length = " + elems.length + "\n"); + } +} diff --git a/src/main/java/org/xbib/jacc/util/Interator.java b/src/main/java/org/xbib/jacc/util/Interator.java new file mode 100644 index 0000000..23c7f56 --- /dev/null +++ b/src/main/java/org/xbib/jacc/util/Interator.java @@ -0,0 +1,11 @@ +package org.xbib.jacc.util; + +/** + * + */ +public abstract class Interator { + + public abstract int next(); + + public abstract boolean hasNext(); +} diff --git a/src/main/java/org/xbib/jacc/util/SCC.java b/src/main/java/org/xbib/jacc/util/SCC.java new file mode 100644 index 0000000..a5ba703 --- /dev/null +++ b/src/main/java/org/xbib/jacc/util/SCC.java @@ -0,0 +1,141 @@ +package org.xbib.jacc.util; + +/** + * + */ +public class SCC { + + public static int[][] get(int[][] ai, int[][] ai1, int i) { + return new GetComponents(ai, i, new ArrangeByFinish(ai1, i).getFinishOrder()).getComponents(); + } + + public static int[][] get(int[][] ai) { + return get(ai, invert(ai), ai.length); + } + + private static int[][] invert(int[][] ai) { + return invert(ai, ai.length); + } + + public static int[][] invert(int[][] ai, int i) { + int[] ai1 = new int[i]; + for (int j = 0; j < i; j++) { + for (int k = 0; k < ai[j].length; k++) { + ai1[ai[j][k]]++; + } + } + int[][] ai2 = new int[i][]; + for (int l = 0; l < i; l++) { + ai2[l] = new int[ai1[l]]; + } + for (int i1 = 0; i1 < i; i1++) { + for (int j1 = 0; j1 < ai[i1].length; j1++) { + int k1 = ai[i1][j1]; + ai1[k1]--; + ai2[k1][ai1[k1]] = i1; + } + } + return ai2; + } + + private static class GetComponents extends DepthFirst { + + private int numComps; + private int[] compNo; + + GetComponents(int[][] ai, int i, int[] ai1) { + super(new ElemInterator(ai1), ai); + numComps = 0; + compNo = new int[i]; + } + + void doneVisit(int i) { + compNo[i] = numComps; + } + + void doneTree() { + numComps++; + } + + int[][] getComponents() { + search(); + int[] ai = new int[numComps]; + for (int aCompNo : compNo) { + ai[aCompNo]++; + } + int ai1[][] = new int[numComps][]; + for (int j = 0; j < numComps; j++) { + ai1[j] = new int[ai[j]]; + } + for (int k = 0; k < compNo.length; k++) { + int l = compNo[k]; + ai1[l][--ai[l]] = k; + } + return ai1; + } + } + + private static class ArrangeByFinish extends DepthFirst { + private int dfsNum; + private int[] order; + + ArrangeByFinish(int[][] ai, int i) { + super(new SeqInterator(0, i), ai); + dfsNum = i; + order = new int[dfsNum]; + } + + void doneVisit(int i) { + order[--dfsNum] = i; + } + + int[] getFinishOrder() { + search(); + return order; + } + } + + abstract static class DepthFirst { + + private Interator seq; + private int[][] adjs; + private int[] visited; + + DepthFirst(Interator interator, int[][] ai) { + seq = interator; + adjs = ai; + visited = BitSet.make(ai.length); + } + + void search() { + do { + if (!seq.hasNext()) { + break; + } + if (visit(seq.next())) { + doneTree(); + } + } while (true); + } + + private boolean visit(int i) { + if (BitSet.addTo(visited, i)) { + int[] ai = adjs[i]; + for (int j : ai) { + visit(j); + } + doneVisit(i); + return true; + } else { + return false; + } + } + + void doneVisit(int i) { + } + + void doneTree() { + } + } + +} diff --git a/src/main/java/org/xbib/jacc/util/SeqInterator.java b/src/main/java/org/xbib/jacc/util/SeqInterator.java new file mode 100644 index 0000000..b1c3568 --- /dev/null +++ b/src/main/java/org/xbib/jacc/util/SeqInterator.java @@ -0,0 +1,23 @@ +package org.xbib.jacc.util; + +/** + * + */ +class SeqInterator extends Interator { + + private int count; + private int limit; + + SeqInterator(int i, int j) { + count = i; + limit = j; + } + + public int next() { + return count++; + } + + public boolean hasNext() { + return count < limit; + } +} diff --git a/src/main/java/org/xbib/jacc/util/package-info.java b/src/main/java/org/xbib/jacc/util/package-info.java new file mode 100644 index 0000000..760b5cf --- /dev/null +++ b/src/main/java/org/xbib/jacc/util/package-info.java @@ -0,0 +1,4 @@ +/** + * Utility classes. + */ +package org.xbib.jacc.util; diff --git a/src/site/resources/Calc.jacc b/src/site/resources/Calc.jacc new file mode 100644 index 0000000..4bcdacc --- /dev/null +++ b/src/site/resources/Calc.jacc @@ -0,0 +1,165 @@ +// To compile and run this program using jacc and Sun's JDK: +// +// In a directory containing only the file Calc.jacc: +// +// jacc Calc.jacc +// javac *.java +// java CalcParser +// ... enter arithmetic expressions ... hit EOF to terminate +// + + +%{ + abstract class Expr { + abstract int eval(); + } + + class IntExpr extends Expr { + private int value; + IntExpr(int value) { this.value = value; } + int eval() { return value; } + } + + abstract class BinExpr extends Expr { + protected Expr left, right; + BinExpr(Expr left, Expr right) { + this.left = left; this.right = right; + } + } + + class AddExpr extends BinExpr { + AddExpr(Expr left, Expr right) { super(left, right); } + int eval() { return left.eval() + right.eval(); } + } + + class SubExpr extends BinExpr { + SubExpr(Expr left, Expr right) { super(left, right); } + int eval() { return left.eval() - right.eval(); } + } + + class MulExpr extends BinExpr { + MulExpr(Expr left, Expr right) { super(left, right); } + int eval() { return left.eval() * right.eval(); } + } + + class DivExpr extends BinExpr { + DivExpr(Expr left, Expr right) { super(left, right); } + int eval() { return left.eval() / right.eval(); } + } + + class CalcLexer implements CalcTokens { + private int c = ' '; + + /** Read a single input character from standard input. + */ + private void nextChar() { + if (c>=0) { + try { + c = System.in.read(); + } catch (Exception e) { + c = (-1); + } + } + } + + private int token; + private IntExpr yylval; + + /** Read the next token and return the + * corresponding integer code. + */ + int nextToken() { + for (;;) { + // Skip whitespace + while (c==' ' || c=='\n' || c=='\t' || c=='\r') { + nextChar(); + } + if (c<0) { + return (token=ENDINPUT); + } + switch (c) { + case '+' : nextChar(); + return token='+'; + case '-' : nextChar(); + return token='-'; + case '*' : nextChar(); + return token='*'; + case '/' : nextChar(); + return token='/'; + case '(' : nextChar(); + return token='('; + case ')' : nextChar(); + return token=')'; + case ';' : nextChar(); + return token=';'; + default : if (Character.isDigit((char)c)) { + int n = 0; + do { + n = 10*n + (c - '0'); + nextChar(); + } while (Character.isDigit((char)c)); + yylval = new IntExpr(n); + return token=INTEGER; + } else { + Main.error("Illegal character "+c); + nextChar(); + } + } + } + } + + /** Return the token code for the current lexeme. + */ + int getToken() { + return token; + } + + /** Return the semantic value for the current lexeme. + */ + IntExpr getSemantic() { + return yylval; + } + } + + class Main { + public static void main(String[] args) { + CalcLexer lexer = new CalcLexer(); + lexer.nextToken(); + CalcParser parser = new CalcParser(lexer); + parser.parse(); + } + + static void error(String msg) { + System.out.println("ERROR: " + msg); + System.exit(1); + } + } +%} + +%semantic Expr +%token '+' '-' '*' '/' '(' ')' ';' INTEGER +%left '+' '-' +%left '*' '/' + +%% + +prog : prog ';' expr { System.out.println($3.eval()); } + | expr { System.out.println($1.eval()); } + ; +expr : expr '+' expr { $$ = new AddExpr($1, $3); } + | expr '-' expr { $$ = new SubExpr($1, $3); } + | expr '*' expr { $$ = new MulExpr($1, $3); } + | expr '/' expr { $$ = new DivExpr($1, $3); } + | '(' expr ')' { $$ = $2; } + | INTEGER { $$ = $1; } + ; + +%% + private CalcLexer lexer; + + CalcParser(CalcLexer lexer) { this.lexer = lexer; } + + private void yyerror(String msg) { + Main.error(yyerrno<0 ? msg : yyerrmsgs[yyerrno]); + } + diff --git a/src/site/resources/Layer2.jacc b/src/site/resources/Layer2.jacc new file mode 100644 index 0000000..8b4895c --- /dev/null +++ b/src/site/resources/Layer2.jacc @@ -0,0 +1,232 @@ +%{ + +/* +Copyright (C) 2006 David Massart and Chea Sereyvath, European Schoolnet + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + + import java.io.*; + import java.lang.String; + import java.util.StringTokenizer; +%} + +%token NL /* newline */ +%token AND /* Second kind of keyword */ +%token LEFT_PATENTHESIS /* Second kind of keyword */ +%token RIGHT_PATENTHESIS /* Second kind of keyword */ +%token CHARSTRING1 /* First kind of keyword */ +%token CHARSTRING2 /* Second kind of keyword */ +%token DOT /* Dot signe*/ +%token OPERATORS /* the operator between the signs */ +%token STANDARD /* the standard to use */ +%token INTEGER /* Integer number */ +%token REAL /* real number */ + +/* The type of rules*/ +%type plql +%type clause +%type keywordClause +%type operand +%type term1 +%type term2 +%type charString1 +%type charString2 +%type integer +%type real + +%type exactClause +%type path +%type operator +%type standard + +%type pathExp +%type selector +%type standard + +%start plql + + +%% +/* rule 2-1 */ +plql: clause { $$ = "rule number = 2-1 : " + $1; + query = $$; + } + ; +/* rule 2-2 */ +clause: keywordClause { + System.out.println("rule number = 2-2.1 : " + $1); + } + |standard DOT exactClause { + $1 = $1 + "." + $3; + System.out.println("rule number = 2-2.2 : " + $1); + } + |LEFT_PATENTHESIS clause RIGHT_PATENTHESIS { + $1 = " ( " + $2 + " ) "; + System.out.println("rule number = 2-2.3 : " + $1); + } + |clause AND clause { + $1 = $1 + " and " + $3; + System.out.println("rule number = 2-2.4 : " + $1); + } + ; +/* rule 2-3 */ +keywordClause: operand { + System.out.println("rule number = 2-3 : " + $1); + } + ; + +/* rule 2-4 */ +operand: term1 { + System.out.println("rule number = 2-4.1 : " + $1); + } + |term2 { + System.out.println("rule number = 2-4.2 : " + $1); + } + |integer { + System.out.println("rule number = 2-4.3 : " + $1); + } + |real { + System.out.println("rule number = 2-4.4 : " + $1); + } + ; + +/* rule 2-5 */ +term1: charString1 { + System.out.println("rule number = 2-5 : " + $1); + } + ; +/* rule 2-6 */ +term2: charString2 { + System.out.println("rule number = 2-6 : " + $1); + } + ; +/* rule 2-7 */ +charString1: CHARSTRING1 { + System.out.println("rule number = 2-7 : " + $1); + } + ; +/* rule 2-8 */ +charString2: CHARSTRING2 { + System.out.println("rule number = 2-8 : " + $1); + } + ; +/* rule 2-9 */ +integer: INTEGER { + System.out.println("rule number = 2-9 : " + $1); + } + ; +/* rule 2-10 */ +real: REAL { + System.out.println("rule number = 2-10 : " + $1); + } + ; + +/* rule 2-11 */ +exactClause: path OPERATORS operand { + $1 = $1 + " " + $2 + " " + $3; + System.out.println("rule number = 2-11.1 : " + $1 ); + } + |pathExp { + System.out.println("rule number = 2-11.2 : " + $1 ); + } + ; + +/* rule 2-12 */ +path: term1 { + System.out.println("rule number = 2-12.1 : " + $1); + } + |path DOT path { + $1 = $1 + $2 + $3; + System.out.println("rule number = 2-12.2 : " + $1); + } + ; + +/* rule 2-13 */ +operator: OPERATORS { + System.out.println("rule number = 2-13 : " + $1); + } + ; + + +/* rule 2-14 */ +standard: STANDARD { + System.out.println("rule number = 2-14.1 : " + $1); + } + + |term1 { + System.out.println("rule number = 2-14.2 : " + $1); + } + ; + +/* rule 2-15 */ +pathExp: path DOT pathExp{ + $1 = $1 + $2 + $3 ; + System.out.println("rule number = 2-15.1 : " + $1); + } + |path DOT LEFT_PATENTHESIS selector AND selector RIGHT_PATENTHESIS { + $1 = $1 + $2 + $3 + " " + $4 + $5 + $6 + " " + $7; + System.out.println("rule number = 2-15.2 : " + $1); + } + ; + +/* rule 2-16 */ +selector: term1 OPERATORS operand { + $1 = $1 + " " + $2 + " " + $3; + System.out.println("rule number = 2-16.1 : "+ $1); + } + |selector AND selector { + $1 = $1 + " " + $2 + " " + $3 ; + System.out.println("rule number = 2-16.2 : "+ $1); + } + |LEFT_PATENTHESIS selector RIGHT_PATENTHESIS { + $1 = $1 + " " + $2 + " " + $3 ; + System.out.println("rule number = 2-16.3 : " + $1); + } + |LEFT_PATENTHESIS selector AND selector RIGHT_PATENTHESIS { + $1 = $1 + " " + $2 + " " + $3 + " " + $4 + " " + $5 ; + System.out.println("rule number = 2-16.4 : " + $1); + } + ; + +%% + + private PlqlLayer2Parser lexer; + private String query ; + + private int yylex () { + int yyl_return = -1; + try { + yylval = new PlqlLayer2AnalyzerVal(0); + yyl_return = lexer.yylex(); + } + catch (IOException e) { + System.err.println("IO error :"+e); + } + return yyl_return; + } + + public void yyerror (String error) { + System.err.println ("Syntax Error\n" + error); + } + + public PlqlLayer2Analyzer(Reader r) {/***/ + lexer = new PlqlLayer2Parser(r, this); + } + + public String getQuery() + { + return query ; + } diff --git a/src/site/resources/Unary.jacc b/src/site/resources/Unary.jacc new file mode 100644 index 0000000..8e45239 --- /dev/null +++ b/src/site/resources/Unary.jacc @@ -0,0 +1,29 @@ +// This file contains an extension to the calculator program in Calc.jacc +// that adds support for unary minus. It is intended as a simple example +// to illustrate jacc's ability to take input from multiple files. +// +// To compile and run this program using jacc and Sun's JDK: +// +// In a directory containing only the files Calc.jacc and Unary.jacc: +// +// jacc Calc.jacc Unary.jacc +// javac *.java +// java Calc +// ... enter arithmetic expressions ... hit EOF to terminate +// + + +%{ + class UminusExpr extends Expr { + private Expr expr; + UminusExpr(Expr expr) { this.expr = expr; } + int eval() { return -expr.eval(); } + } +%} + +%left UMINUS + +%% + +expr : '-' expr %prec UMINUS { $$ = new UminusExpr($2); } + ; diff --git a/src/site/resources/dang.ex b/src/site/resources/dang.ex new file mode 100644 index 0000000..961bcef --- /dev/null +++ b/src/site/resources/dang.ex @@ -0,0 +1,7 @@ + + IF expr THEN + IF expr THEN + stmt + ELSE + stmt + diff --git a/src/site/resources/dang.jacc b/src/site/resources/dang.jacc new file mode 100644 index 0000000..22ad858 --- /dev/null +++ b/src/site/resources/dang.jacc @@ -0,0 +1,7 @@ +%token IF THEN ELSE expr other +%% +stmt : IF expr THEN stmt ELSE stmt + | IF expr THEN stmt + | other + ; + diff --git a/src/site/resources/example1 b/src/site/resources/example1 new file mode 100644 index 0000000..3fdbe9a --- /dev/null +++ b/src/site/resources/example1 @@ -0,0 +1,4 @@ + +INTEGER '+' INTEGER '*' INTEGER ';' +INTEGER '*' INTEGER '+' INTEGER + diff --git a/src/site/resources/example2 b/src/site/resources/example2 new file mode 100644 index 0000000..980b9f4 --- /dev/null +++ b/src/site/resources/example2 @@ -0,0 +1,4 @@ + +expr '+' expr '*' expr ';' +expr '*' expr '+' expr + diff --git a/src/site/resources/jacc.pdf b/src/site/resources/jacc.pdf new file mode 100644 index 0000000..acca4f3 Binary files /dev/null and b/src/site/resources/jacc.pdf differ diff --git a/src/site/resources/simpleCalc.jacc b/src/site/resources/simpleCalc.jacc new file mode 100644 index 0000000..f159c15 --- /dev/null +++ b/src/site/resources/simpleCalc.jacc @@ -0,0 +1,104 @@ +// To compile and run this program using jacc and Sun's JDK: +// +// jacc simpleCalc.jacc +// javac Calc.java CalcTokens.java +// java Calc +// ... enter arithmetic expressions ... hit EOF to terminate +// + +%class Calc +%interface CalcTokens +%semantic int : yylval +%get token +%next yylex() + +%token '+' '-' '*' '/' '(' ')' ';' INTEGER +%left '+' '-' +%left '*' '/' + +%% + +prog : prog ';' expr { System.out.println($3); } + | expr { System.out.println($1); } + ; +expr : expr '+' expr { $$ = $1 + $3; } + | expr '-' expr { $$ = $1 - $3; } + | expr '*' expr { $$ = $1 * $3; } + | expr '/' expr { $$ = $1 / $3; } + | '(' expr ')' { $$ = $2; } + | INTEGER { $$ = $1; } + ; + +%% + + private void yyerror(String msg) { + System.out.println("ERROR: " + msg); + System.exit(1); + } + + private int c; + + /** Read a single input character from standard input. + */ + private void nextChar() { + if (c>=0) { + try { + c = System.in.read(); + } catch (Exception e) { + c = (-1); + } + } + } + + int token; + int yylval; + + /** Read the next token and return the + * corresponding integer code. + */ + int yylex() { + for (;;) { + // Skip whitespace + while (c==' ' || c=='\n' || c=='\t' || c=='\r') { + nextChar(); + } + if (c<0) { + return (token=ENDINPUT); + } + switch (c) { + case '+' : nextChar(); + return token='+'; + case '-' : nextChar(); + return token='-'; + case '*' : nextChar(); + return token='*'; + case '/' : nextChar(); + return token='/'; + case '(' : nextChar(); + return token='('; + case ')' : nextChar(); + return token=')'; + case ';' : nextChar(); + return token=';'; + default : if (Character.isDigit((char)c)) { + int n = 0; + do { + n = 10*n + (c - '0'); + nextChar(); + } while (Character.isDigit((char)c)); + yylval = n; + return token=INTEGER; + } else { + yyerror("Illegal character "+c); + nextChar(); + } + } + } + } + + public static void main(String[] args) { + Calc calc = new Calc(); + calc.nextChar(); // prime the character input stream + calc.yylex(); // prime the token input stream + calc.parse(); // parse the input + }