diff --git a/.gitignore b/.gitignore index e16b0553f..3edac033f 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,8 @@ **/target dependency-reduced-pom.xml bazel-* +# Generated by ant +jflex/build # IntelliJ *.iml diff --git a/.lgtm.yml b/.lgtm.yml new file mode 100644 index 000000000..e33dbae1a --- /dev/null +++ b/.lgtm.yml @@ -0,0 +1,17 @@ +# Configutation for LGTM +# https://lgtm.com/projects/g/jflex-de/jflex/ +# Format at https://help.semmle.com/lgtm-enterprise/user/help/lgtm.yml-configuration-file.html + +# Note that results for all files classified with a tag are hidden. +# Path syntax is ant matcher https://confluence.atlassian.com/fisheye/pattern-matching-guide-298976797.html +path_classifiers: + test: + - "src/test" + docs: + - "LICENSE*" + - "*.md" + third_party: + - "/cup" + - "/third_party" + generated: + - "/jflex/src/main/java/jflex/unicode/data/Unicode_*.java" diff --git a/.mvn/wrapper/MavenWrapperDownloader.java b/.mvn/wrapper/MavenWrapperDownloader.java new file mode 100755 index 000000000..fa4f7b499 --- /dev/null +++ b/.mvn/wrapper/MavenWrapperDownloader.java @@ -0,0 +1,110 @@ +/* +Licensed to the Apache Software Foundation (ASF) under one +or more contributor license agreements. See the NOTICE file +distributed with this work for additional information +regarding copyright ownership. The ASF licenses this file +to you under the Apache License, Version 2.0 (the +"License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, +software distributed under the License is distributed on an +"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, either express or implied. See the License for the +specific language governing permissions and limitations +under the License. +*/ + +import java.net.*; +import java.io.*; +import java.nio.channels.*; +import java.util.Properties; + +public class MavenWrapperDownloader { + + /** + * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. + */ + private static final String DEFAULT_DOWNLOAD_URL = + "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar"; + + /** + * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to + * use instead of the default one. + */ + private static final String MAVEN_WRAPPER_PROPERTIES_PATH = + ".mvn/wrapper/maven-wrapper.properties"; + + /** + * Path where the maven-wrapper.jar will be saved to. + */ + private static final String MAVEN_WRAPPER_JAR_PATH = + ".mvn/wrapper/maven-wrapper.jar"; + + /** + * Name of the property which should be used to override the default download url for the wrapper. + */ + private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; + + public static void main(String args[]) { + System.out.println("- Downloader started"); + File baseDirectory = new File(args[0]); + System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); + + // If the maven-wrapper.properties exists, read it and check if it contains a custom + // wrapperUrl parameter. + File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); + String url = DEFAULT_DOWNLOAD_URL; + if(mavenWrapperPropertyFile.exists()) { + FileInputStream mavenWrapperPropertyFileInputStream = null; + try { + mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); + Properties mavenWrapperProperties = new Properties(); + mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); + url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); + } catch (IOException e) { + System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); + } finally { + try { + if(mavenWrapperPropertyFileInputStream != null) { + mavenWrapperPropertyFileInputStream.close(); + } + } catch (IOException e) { + // Ignore ... + } + } + } + System.out.println("- Downloading from: : " + url); + + File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); + if(!outputFile.getParentFile().exists()) { + if(!outputFile.getParentFile().mkdirs()) { + System.out.println( + "- ERROR creating output direcrory '" + outputFile.getParentFile().getAbsolutePath() + "'"); + } + } + System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); + try { + downloadFileFromURL(url, outputFile); + System.out.println("Done"); + System.exit(0); + } catch (Throwable e) { + System.out.println("- Error downloading"); + e.printStackTrace(); + System.exit(1); + } + } + + private static void downloadFileFromURL(String urlString, File destination) throws Exception { + URL website = new URL(urlString); + ReadableByteChannel rbc; + rbc = Channels.newChannel(website.openStream()); + FileOutputStream fos = new FileOutputStream(destination); + fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); + fos.close(); + rbc.close(); + } + +} diff --git a/.mvn/wrapper/maven-wrapper.jar b/.mvn/wrapper/maven-wrapper.jar index f775b1c04..01e679973 100755 Binary files a/.mvn/wrapper/maven-wrapper.jar and b/.mvn/wrapper/maven-wrapper.jar differ diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties index a447c9fa8..00d32aab1 100755 --- a/.mvn/wrapper/maven-wrapper.properties +++ b/.mvn/wrapper/maven-wrapper.properties @@ -1 +1 @@ -distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.5.2/apache-maven-3.5.2-bin.zip \ No newline at end of file +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.5.4/apache-maven-3.5.4-bin.zip \ No newline at end of file diff --git a/.travis.bazelrc b/.travis.bazelrc new file mode 100644 index 000000000..a34c9f070 --- /dev/null +++ b/.travis.bazelrc @@ -0,0 +1,9 @@ +# This is from Bazel's former travis setup, to avoid blowing up the RAM usage. +startup --host_jvm_args=-Xms2000m +startup --host_jvm_args=-Xmx3000m +test --ram_utilization_factor=10 + +# This is so we understand failures better +build --verbose_failures +test --test_output=errors + diff --git a/.travis.yml b/.travis.yml index 29fb24da4..b18cbb064 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,41 +5,109 @@ branches: - aggregated-java-sources - travis +# We don't use the git log, a very small depth is sufficient +git: + depth: 3 + language: java -# TODO(regisd) Remove the build matrix ; it really makes the deploy phase more complex jdk: -- openjdk7 -- oraclejdk8 +- openjdk9 -env: - matrix: - - TEST_SUITE=ant - - TEST_SUITE=unit - - TEST_SUITE=regression +matrix: + include: + - name: "🛂 Check Java format" + script: scripts/test-java-format.sh + language: generic + - name: "🔨 Maven (compile, unit test, uberjar, ubersrcs, site)" + script: + - scripts/test-unit.sh + - scripts/mvn-site.sh + - scripts/mvn-aggregate-srcs.sh + after_success: scripts/send-code-coverage.sh + env: + - PUBLISH_SOURCES=true + language: generic + - name: "📝 Regression tests" + script: + - scripts/mvn-install-fastbuild.sh jflex-maven-plugin,testsuite/jflex-testsuite-maven-plugin + - scripts/test-regression.sh + - name: "😎 Examples (mvn, ant, make)" + script: scripts/test-examples.sh + - name: "👴 Examples (mvn, ant, make) — JDK7" + script: scripts/test-examples.sh + jdk: openjdk7 + - name: "💚 Bazel (Examples and documentation)" + language: generic + script: + - scripts/mvn-install-fastbuild.sh + - scripts/bazel.sh + # Prerequisites for Bazel + addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - curl + - pkg-config + - zip + - g++ + - zlib1g-dev + - unzip + - python + before_install: + - mkdir -p tools + - curl -L https://github.com/bazelbuild/bazel/releases/download/0.17.2/bazel_0.17.2-linux-x86_64.deb -o tools/bazel-0.17.2.deb + install: + - sudo apt-get install ./tools/bazel-0.17.2.deb + sudo: true + - name: "📄 Documentation" + language: generic + install: true + addons: + apt: + packages: + # pandoc used for building the doc + - pandoc + - pandoc-citeproc + # texlive used for PDF output + - texlive + # texlive-latex-extra provides extra styles such as a4wide and upquote.sty + - texlive-latex-extra + # lmodern.sty + - lmodern + script: + - cd docs; make; cd .. # Empty the previously built artifacts # They cannot be deleted in the before_cache phase, # otherwise `mvn site` fails in the before_deploy phase. -before_install: ./scripts/clean.sh - -install: ./mvnw -version +before_install: +- ./scripts/clean.sh -script: ./scripts/run-tests.sh +install: +- java -version +- javac -version +- ./mvnw -version -after_success: -- ./scripts/mvn-site.sh -- ./scripts/mvn-aggregate-srcs.sh +script: scripts/run-tests.sh # Travis sometimes fails to download deps from repo1.maven.org # A cache avoids downloading too much, and will also speed up the build. # NB: There is one cache per branch and language version/ compiler version/ JDK version cache: + apt: true # The timeout (in seconds) empties the cache to avoid being stuck with a corrupted artefact timeout: 86400 # 24 hours directories: - $HOME/.m2 + # All our Bazel build artifacts + - $HOME/__bazel_output_base__/ + # Items fetched from repositories + - $HOME/__bazel_travis_root__/cache + # DO NOT cache __bazel_output_travis__/install - jflex/lib + - tools deploy: # Deploy the maven site on Github pages @@ -49,7 +117,7 @@ deploy: branch: release condition: - $TEST_SUITE = unit - - $TRAVIS_JDK_VERSION = oraclejdk8 + - $TRAVIS_JDK_VERSION = openjdk9 local_dir: target/maven-staging-site/maven-site # GITHUB_TOKEN set in travis-ci.org dashboard github_token: $GITHUB_TOKEN @@ -64,6 +132,5 @@ deploy: - master - travis condition: - - $TEST_SUITE = unit - - $TRAVIS_JDK_VERSION = oraclejdk8 + - $PUBLISH_SOURCES script: ./scripts/deploy-source-code.sh diff --git a/CODEOWNERS b/CODEOWNERS new file mode 100644 index 000000000..53f8b359e --- /dev/null +++ b/CODEOWNERS @@ -0,0 +1,13 @@ +# Default owner for everything, unless a later match takes precedence +# This project was created by Gerwin +* @lsf37 + +# In general, we don't wan't to change code in cup. +# Be sure we have a big red warning by giving ownership to nobody. +/cup/ @issues + +# The Maven plugin was initally authored by Régis +/jflex-maven-plugin/ @regisd +# Régis alsi knows more Bazel confg +WORKSPACE @regis +BUILD @regisd diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index afb9d3435..000000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,64 +0,0 @@ -# Contribution -## Contributions are welcome - -JFlex is free software and contributions are welcome. - -The preferred way to contribute code changes or fixes is via github pull -requests. Before you send a pull request, please make sure that unit tests and -regression test suite pass. - -## Code style - -JFlex follows the [Google-style][google-style] for Java. - -This is enforced by the [fmt-maven-plugin][fmt-maven-plugin]. - -You can also use a configuration style - -* [Google style for Eclipse][google-style-eclipse] -* [Google style for IntelliJ][google-style-intellij] - -## Setting up your environment - -We provide help on how to set up your -[development environment](https://github.com/jflex-de/jflex/wiki/Development-environment). - -## Running the test suites - -### Short version - - ./run-tests - -This runs the entire test suites, including unit and regression tests, as -well as examples. - -### Longer version - -Unit tests are run automatically if you locally install the jflex package for -instance via - - mvn -N install - mvn install - -The regression test suite must be run from the directory -`testsuite/testcases`, for instance with - - mvn test - -The jflex/examples can be run individually either by calling `make` or `mvn -test`. - - -## Get in touch - -If you have larger code changes or new features to contribute it is usually a -good idea to get in touch with the developers first and discuss if your idea -fits with the rest of the JFlex design. You can get in contact either on the -github issue tracking system or the [JFlex users mailing list][ml]. - -[ml]: http://jflex.de/mailing.html -[fmt-maven-plugin]: https://github.com/coveo/fmt-maven-plugin -[google-style]: https://github.com/google/styleguide/ -[google-style-eclipse]: https://github.com/google/styleguide/blob/gh-pages/eclipse-java-google-style.xml -[google-style-intellij]: https://github.com/google/styleguide/blob/gh-pages/intellij-java-google-style.xml - diff --git a/README.md b/README.md index 8464a9e8a..fef8dc2cf 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,9 @@ -[![Build Status](https://travis-ci.org/jflex-de/jflex.svg?branch=master)](https://travis-ci.org/jflex-de/jflex) + + Build status + + + Maven central + # JFlex @@ -13,6 +18,7 @@ operators, etc, and generating an input token stream for parsers. JFlex lexers are based on deterministic finite automata (DFAs). They are fast, without expensive backtracking. + ## Modules The top level directory of the JFLex git repository contains: @@ -24,6 +30,8 @@ The top level directory of the JFLex git repository contains: * **jflex-maven-plugin** the JFlex maven plugin, that helps to integrate JFlex in your project * **jflex-unicode-plugin** the JFlex unicode maven plugin, used for compiling JFlex * **testsuite** the regression test suite for JFlex, + * **third_party** third-party librairies used by examples of the [Bazel build system][bazel] + ## Usage @@ -57,8 +65,6 @@ and the [wiki][wiki]. 3. Voilà: Java code is produced in `target/generated-sources/` during the `generate-sources` phase (which happens before the `compile` phase) and included in the compilation scope. -Sample project: [simple-maven][example-simple-maven] - ### Usage with ant 1. Define ant task @@ -72,6 +78,19 @@ Sample project: [simple-maven][example-simple-maven] ``` +### Usage with Bazel + +We provide a [jflex rule](https://jflex-de.github.io/bazel_rules/) + +``` +load("@jflex_rules//jflex:jflex.bzl", "jflex") +jflex( + name = "", # Choose a rule name + srcs = [], # Add input lex specifications + outputs = [], # List expected generated files +) +``` + ### Usage in CLI You can also use JFlex directly from the command line: @@ -84,20 +103,34 @@ Or: java -jar jflex-full-1.7.0.jar -d output src/grammar/parser.flex ``` +### Other build tools + +See [Build tool plugins](https://github.com/jflex-de/jflex/wiki/Build-tool-integration). + + +## Examples + +Have a look at the sample project: [simple][example-simple] and other [examples]. + + ## Build from source ``` ./mvnw install ``` + ## Contributing JFlex is free software, contributions are welcome. -See the file [CONTRIBUTING.md](CONTRIBUTING.md) for instructions. +See the [Contributing][contrib] page for instructions. [jflex]: http://jflex.de/ [jflex-doc]: http://jflex.de/manual.html [wiki]: https://github.com/jflex-de/jflex/wiki [pom-build]: https://maven.apache.org/pom.html#Build_Settings -[example-simple-maven]: https://github.com/jflex-de/jflex/tree/master/jflex/examples/simple-maven +[example-simple]: https://github.com/jflex-de/jflex/tree/master/jflex/examples/simple +[examples]: https://github.com/jflex-de/jflex/tree/master/jflex/examples/ +[contrib]: https://github.com/jflex-de/jflex/wiki/Contributing +[bazel]: http://bazel.build/ diff --git a/WORKSPACE b/WORKSPACE new file mode 100644 index 000000000..346fc68fe --- /dev/null +++ b/WORKSPACE @@ -0,0 +1,52 @@ +# Workspace file for the Bazel build system +# https://bazel.build/ + +# JFlex itself is not built with Bazel, but some examples and the documentation are. +load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository") + +git_repository( + name = "jflex_rules", + branch = "stable", + remote = "https://github.com/jflex-de/bazel_rules.git", +) + +load("@jflex_rules//jflex:deps.bzl", "jflex_deps") + +jflex_deps() + +# pandoc used to build the documentatoin + +# TODO(regisd) Take upstream when they have accepted my PR to allow specifying output +# https://github.com/ProdriveTechnologies/bazel-pandoc/pull/1 +#http_archive( +# name = "bazel_pandoc", +# strip_prefix = "bazel-pandoc-0.1", +# url = "https://github.com/ProdriveTechnologies/bazel-pandoc/archive/v0.1.tar.gz", +#) +http_archive( + name = "bazel_pandoc", + sha256 = "0dd9d0d44658d46a96c36caba25f7ce9f119a6883c3219f61b76c11cfdc83c8f", + strip_prefix = "bazel_pandoc-0.1.1", + url = "https://github.com/regisd/bazel_pandoc/archive/v0.1.1.tar.gz", +) + +load("@bazel_pandoc//:repositories.bzl", "pandoc_repositories") + +pandoc_repositories() + +# latex rule to build PDF from tex files + +http_archive( + name = "bazel_latex", + strip_prefix = "bazel-latex-0.9", + url = "https://github.com/ProdriveTechnologies/bazel-latex/archive/v0.9.tar.gz", +) + +load("@bazel_latex//:repositories.bzl", "latex_repositories") + +latex_repositories() + +# Third-party depenencies +load("//third_party:deps.bzl", "third_party_deps") + +third_party_deps() diff --git a/cup-maven-plugin/README.md b/cup-maven-plugin/README.md new file mode 100644 index 000000000..f1eaa22a9 --- /dev/null +++ b/cup-maven-plugin/README.md @@ -0,0 +1,34 @@ +# CUP Maven plugin + +This is a plugin to invoke cup from Maven. + +Version 1.0 of the plugin uses CUP 11b. + +## Usage + +By default, this plugin runs + +``` +cup -destdir target/generated-sources/cup -parser parser -symbols sym src/main/cup/*.cup +``` + +```xml + + de.jflex + cup-maven-plugin + 1.0 + + + + generate + + + + + + +``` + +## Example + +Please see [cup/sample-project](/jflex-de/jflex/cup/sample-project/) diff --git a/cup-maven-plugin/pom.xml b/cup-maven-plugin/pom.xml index 04f706fc6..397d581cd 100644 --- a/cup-maven-plugin/pom.xml +++ b/cup-maven-plugin/pom.xml @@ -76,4 +76,17 @@ test + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.7 + 1.7 + + + + diff --git a/cup-maven-plugin/src/main/java/de/jflex/plugin/cup/GenerateMojo.java b/cup-maven-plugin/src/main/java/de/jflex/plugin/cup/GenerateMojo.java index 98e790015..d885223a8 100644 --- a/cup-maven-plugin/src/main/java/de/jflex/plugin/cup/GenerateMojo.java +++ b/cup-maven-plugin/src/main/java/de/jflex/plugin/cup/GenerateMojo.java @@ -149,12 +149,13 @@ private boolean isGeneratedCodeOutdated(File cupFile, String javaPackage) { } private String findJavaPackage(File cupFile) throws IOException { - BufferedReader br = new BufferedReader(new FileReader(cupFile)); - while (br.ready()) { - String line = br.readLine(); - Optional optJavaPackage = optionalJavaPackage(line); - if (optJavaPackage.isPresent()) { - return optJavaPackage.get(); + try (BufferedReader br = new BufferedReader(new FileReader(cupFile))) { + while (br.ready()) { + String line = br.readLine(); + Optional optJavaPackage = optionalJavaPackage(line); + if (optJavaPackage.isPresent()) { + return optJavaPackage.get(); + } } } return DEFAULT_JAVA_PACKAGE; diff --git a/cup/BUILD b/cup/BUILD new file mode 100644 index 000000000..ff7e3c245 --- /dev/null +++ b/cup/BUILD @@ -0,0 +1,22 @@ +package(default_visibility = ["//visibility:public"]) + +licenses(["notice"]) # GPL-compatible + +java_binary( + name = "cup_bin", + main_class = "java_cup.Main", + runtime_deps = [ + ":cup", + ], +) + +# This is the full Java CUP (with runtime) +java_import( + name = "cup", + jars = ["cup/java-cup-11b.jar"], +) + +alias( + name = "cup_runtime", + actual = "//cup/cup_runtime", +) diff --git a/cup/LICENSE b/cup/LICENSE deleted file mode 100644 index 2b071cd64..000000000 --- a/cup/LICENSE +++ /dev/null @@ -1,15 +0,0 @@ -CUP Parser Generator Copyright Notice, License, and Disclaimer - -Copyright 1996-2015 by Scott Hudson, Frank Flannery, C. Scott Ananian, Michael Petter -Permission to use, copy, modify, and distribute this software and its documentation for any purpose -and without fee is hereby granted, provided that the above copyright notice appear in all copies and -that both the copyright notice and this permission notice and warranty disclaimer appear in -supporting documentation, and that the names of the authors or their employers not be used in -advertising or publicity pertaining to distribution of the software without specific, written prior -permission. - -The authors and their employers disclaim all warranties with regard to this software, including all -implied warranties of merchantability and fitness. In no event shall the authors or their employers -be liable for any special, indirect or consequential damages or any damages whatsoever resulting -from loss of use, data or profits, whether in an action of contract, negligence or other tortious -action, arising out of or in connection with the use or performance of this software. diff --git a/cup/LICENSE b/cup/LICENSE new file mode 120000 index 000000000..b461f6488 --- /dev/null +++ b/cup/LICENSE @@ -0,0 +1 @@ +cup_runtime/src/main/resources/LICENSE_CUP \ No newline at end of file diff --git a/cup/README.md b/cup/README.md index 91fcb04d0..384a03a5a 100644 --- a/cup/README.md +++ b/cup/README.md @@ -11,4 +11,15 @@ Home directory and parent POM of 2 artefacts: Note that this code is excluded from many rules of our codebase, for instance google-java-format is not applied. + +## Bazel rule + + load("//cup:cup.bzl", "cup") + + cup( + name = "rule_name", + src = "spec.cup", + ) + + [cup]: http://www2.cs.tum.edu/projects/cup/ diff --git a/cup/cup.bzl b/cup/cup.bzl new file mode 100644 index 000000000..804f9c915 --- /dev/null +++ b/cup/cup.bzl @@ -0,0 +1,24 @@ +"""Bazel rules for cup. """ + +# CUP can only read from stdin, which Skylark rules don't support. Use a genrule for now. +def cup(name, src, parser = "Parser", symbols = "Symbols", interface = False): + """Generate a parser with CUP. + + Args: + name: name of the rule + srcs: list of cup specifications + parser: name of the generated parser class + symbols: name of the generated symbols class + interface: whether to generate an interface + """ + opts = "-parser {parser} -symbols {symbols}".format(parser = parser, symbols = symbols) + if interface: + opts = opts + " -interface" + cmd = ("$(location //cup:cup_bin) -destdir $(@D) " + opts + " < $<") + native.genrule( + name = name, + srcs = [src], + tools = ["//cup:cup_bin"], + outs = [parser + ".java", symbols + ".java"], + cmd = cmd, + ) diff --git a/cup/cup/pom.xml b/cup/cup/pom.xml index ed6d19986..ca07601bf 100644 --- a/cup/cup/pom.xml +++ b/cup/cup/pom.xml @@ -15,6 +15,13 @@ + + org.apache.maven.plugins + maven-deploy-plugin + + true + + org.apache.maven.plugins maven-install-plugin diff --git a/cup/cup_runtime/BUILD b/cup/cup_runtime/BUILD new file mode 100644 index 000000000..c9eea2124 --- /dev/null +++ b/cup/cup_runtime/BUILD @@ -0,0 +1,6 @@ +licenses(["notice"]) # BSD-like +java_library( + name = "cup_runtime", + srcs = glob(["src/main/java/**/*.java"]), + visibility=["//visibility:public"] +) \ No newline at end of file diff --git a/cup/cup_runtime/src/main/resources/LICENSE_CUP b/cup/cup_runtime/src/main/resources/LICENSE_CUP new file mode 100644 index 000000000..2b071cd64 --- /dev/null +++ b/cup/cup_runtime/src/main/resources/LICENSE_CUP @@ -0,0 +1,15 @@ +CUP Parser Generator Copyright Notice, License, and Disclaimer + +Copyright 1996-2015 by Scott Hudson, Frank Flannery, C. Scott Ananian, Michael Petter +Permission to use, copy, modify, and distribute this software and its documentation for any purpose +and without fee is hereby granted, provided that the above copyright notice appear in all copies and +that both the copyright notice and this permission notice and warranty disclaimer appear in +supporting documentation, and that the names of the authors or their employers not be used in +advertising or publicity pertaining to distribution of the software without specific, written prior +permission. + +The authors and their employers disclaim all warranties with regard to this software, including all +implied warranties of merchantability and fitness. In no event shall the authors or their employers +be liable for any special, indirect or consequential damages or any damages whatsoever resulting +from loss of use, data or profits, whether in an action of contract, negligence or other tortious +action, arising out of or in connection with the use or performance of this software. diff --git a/cup/pom.xml b/cup/pom.xml index 315033395..d2f66a6e1 100644 --- a/cup/pom.xml +++ b/cup/pom.xml @@ -50,13 +50,18 @@ + - org.apache.maven.plugins - maven-deploy-plugin + maven-compiler-plugin - true + 1.6 + 1.6 + + org.apache.maven.plugins + maven-deploy-plugin + org.apache.maven.plugins maven-site-plugin @@ -67,6 +72,10 @@ + + maven-compiler-plugin + 3.7.0 + maven-deploy-plugin 2.8.2 @@ -87,6 +96,14 @@ maven-site-plugin 3.6 + + maven-javadoc-plugin + 2.10.1 + + + maven-source-plugin + 3.0.1 + @@ -96,6 +113,51 @@ + + release + + + + maven-javadoc-plugin + + + attach-javadocs + + jar + + + + + + maven-source-plugin + + + attach-sources + + jar-no-fork + + + + + + maven-gpg-plugin + 1.6 + + 84A70085 + + + + sign-artifacts + verify + + sign + + + + + + + no-doclint @@ -108,6 +170,14 @@ + + ossrh + https://oss.sonatype.org/content/repositories/snapshots + + + ossrh + https://oss.sonatype.org/service/local/staging/deploy/maven2/ + github.pages Github pages diff --git a/cup/sample-project/BUILD b/cup/sample-project/BUILD new file mode 100644 index 000000000..adac8145b --- /dev/null +++ b/cup/sample-project/BUILD @@ -0,0 +1,6 @@ +load("//cup:cup.bzl", "cup") + +cup( + name = "gen_parser", + src = "src/main/cup/calculator.cup", +) diff --git a/cup/sample-project/REAME.md b/cup/sample-project/README.md similarity index 100% rename from cup/sample-project/REAME.md rename to cup/sample-project/README.md diff --git a/docs/BUILD b/docs/BUILD new file mode 100644 index 000000000..fd8a1ae95 --- /dev/null +++ b/docs/BUILD @@ -0,0 +1,91 @@ +# Build the user manual + +load("@bazel_pandoc//:pandoc.bzl", "pandoc") +load("@bazel_latex//:latex.bzl", "latex_document") + +VERSION = "1.7.1-SNAPSHOT" + +RELEASE_DATE = "21 September 2018" + +UNICODE_VER = "9.0" + +GENRULE_CONCATENATE = "cat $(SRCS) > $@" + +pandoc( + name = "html", + src = ":manual_mdx", + from_format = "markdown", + # TODO: Add "--css manual.css" when the rule accepts data files + # + # TODO: Add "+smart" when the toolchain supports it + # + # TODO: Add ["--filter", "pandoc-citeproc"] when #2 is fixed + # https://github.com/ProdriveTechnologies/bazel-pandoc/issues/2 + options = [], + output = "manual.html", + to_format = "html", +) + +pandoc( + name = "latex_content", + src = ":manual_mdx", + from_format = "markdown", + output = "content.tex", # If changed, then change \include{} in manual.tex + to_format = "latex", +) + +genrule( + name = "manual_full_tex", + srcs = [ + "manual_start.tex", + ":latex_content", + "manual_end.tex", + ], + outs = ["manual_full.tex"], + cmd = GENRULE_CONCATENATE, +) + +latex_document( + name = "manual", + srcs = [ + #"manual.tex", + # "minimal.tex", + # "manual_full.tex", + "@bazel_latex//packages:graphicx", + "@bazel_latex//packages:hyperref", + "@bazel_latex//packages:microtype", + ], + main = "manual_full.tex", +) + +# Replaces placeholders by their respective value. +genrule( + name = "manual_mdx", + srcs = [":concatenated_manual"], + outs = ["manual.mdx"], + cmd = "sed -e 's/\$$VERSION/" + VERSION + "/g'" + + " -e 's/\$${project.version}/" + VERSION + "/g'" + + " -e 's/\$$RELEASE_DATE/" + RELEASE_DATE + "/g'" + + " -e 's/\$$UNICODE_VER/" + UNICODE_VER + "/g'" + + " $< > $@", +) + +# Concatenates all pages in a single document. +genrule( + name = "concatenated_manual", + srcs = [ + "md/head.md", + "md/intro.md", + "md/installing.md", + "md/maven-plugin.md", + "md/ant-task.md", + "md/example.md", + "md/lex-specs.md", + "md/encodings.md", + "md/performance.md", + "md/porting-and-parsers.md", + "md/end.md", + ], + outs = ["concatenated_manual.md"], + cmd = GENRULE_CONCATENATE, +) diff --git a/docs/Makefile b/docs/Makefile index 1d776181d..3c4a8d5df 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -36,9 +36,10 @@ manual.pdf: xmanual.pdf # variable substitutions: %.mdx: md/%.md Makefile - perl -p -e 's/\$$VERSION/$(VERSION)/g' < $< | \ - perl -p -e 's/\$$RELEASE_DATE/$(RELEASE_DATE)/g' | \ - perl -p -e 's/\$$UNICODE_VER/$(UNICODE_VER)/g' > $@ + sed -e 's/\$$VERSION/$(VERSION)/g' \ + -e 's/\$${project.version}/$(VERSION)/g' \ + -e 's/\$$RELEASE_DATE/$(RELEASE_DATE)/g' \ + -e 's/\$$UNICODE_VER/$(UNICODE_VER)/g' $< > $@ %.tex: %.mdx pandoc -f markdown -t latex --biblatex $< -o $@ diff --git a/docs/manual.tex b/docs/manual.tex new file mode 100644 index 000000000..f22380b3e --- /dev/null +++ b/docs/manual.tex @@ -0,0 +1,83 @@ +% The Latex template for the manual, when doc built with bazel + +\documentclass[11pt] +%{scrartcl} +%\usepackage{a4wide} +%\usepackage{verbatim} +%\usepackage{graphicx} % "@bazel_latex//packages:graphicx" +%\usepackage{upquote} +%\usepackage{microtype} % "@bazel_latex//packages:microtype" + +%\usepackage{color} +\definecolor{lcol}{rgb}{0,0,0.5} +%\usepackage[unicode=true,bookmarks, +% colorlinks=true,linkcolor=lcol,citecolor=lcol, +% filecolor=lcol,urlcolor=lcol, +% pdfauthor={Gerwin Klein, Steve Rowe, Regis Decamps}, +% pdftitle={JFlex User's Manual}, +% pdfkeywords={Java, scanner, lexer, scanner generator}, +% plainpages=false]{hyperref} + + +% pandoc packages +\usepackage{lmodern} +\usepackage{fixltx2e} % provides \textsubscript +\usepackage[T1]{fontenc} +\usepackage[utf8]{inputenc} + +\newcommand{\textcite}[1]{\cite{#1}} +\newcommand{\autocite}[1]{\cite{#1}} + +\renewcommand{\textquotesingle}{'} + +\let\orighyperref\hyperref +\renewcommand{\hyperref}[2][]{\orighyperref[#1]{#2} (\autoref{#1})} + +\let\ttf\ttfamily +\renewcommand{\ttfamily}{\small\ttf} + +\renewcommand{\sectionautorefname}{Section} +\renewcommand{\subsectionautorefname}{Section} +\renewcommand{\subsubsectionautorefname}{Section} +\renewcommand{\Hfootnoteautorefname}{Footnote} + +\setlength{\parindent}{0pt} +\setlength{\parskip}{1ex plus 0.5ex minus 0.2ex} + +% pandoc now uses \tightlist +\providecommand{\tightlist}{% + \setlength{\itemsep}{0pt}\setlength{\parskip}{0pt}} + +\begin{document} + +% TODO Find real bath of image +% can't find file fix.jflex-black.png +%\centerline{\includegraphics[width=0.6\textwidth]{fig/jflex-black.png}} + +\begin{center} +\sffamily +{\Large The Fast Lexical Analyser Generator}\\ +\smallskip\smallskip +Copyright \copyright\ 1998--2018 +by \href{http://www.doclsf.de}{Gerwin Klein}, +Steve Rowe, +and \href{http://regis.decamps.info/}{R\'egis D\'ecamps}. + +\vspace*{15ex} +{\Huge \sffamily \bfseries JFlex User's Manual}\\ +\bigskip +Version 1.7.1-SNAPSHOT, {\today} +\end{center} + +\newpage +\tableofcontents +\newpage + +\include{content} + +\newpage + +\bibliographystyle{plain} +\bibliography{manual} + +\end{document} diff --git a/docs/manual_end.tex b/docs/manual_end.tex new file mode 100644 index 000000000..1aad1859f --- /dev/null +++ b/docs/manual_end.tex @@ -0,0 +1,2 @@ + +\end{document} \ No newline at end of file diff --git a/docs/manual_start.tex b/docs/manual_start.tex new file mode 100644 index 000000000..455843755 --- /dev/null +++ b/docs/manual_start.tex @@ -0,0 +1,10 @@ +\documentclass{article} +\usepackage{hyperref} + +% pandoc now uses \tightlist +\providecommand{\tightlist}{% + \setlength{\itemsep}{0pt}\setlength{\parskip}{0pt}} + +\newcommand{\textquotesingle}{'} + +\begin{document} diff --git a/docs/md/head.md b/docs/md/head.md index 1fbc4cb34..87094a07d 100644 --- a/docs/md/head.md +++ b/docs/md/head.md @@ -3,8 +3,9 @@ title: JFlex User's Manual bibliography: manual.bib --- +TODO: Restore image +
-![](fig/jflex-black.png) The Fast Lexical Analyser Generator diff --git a/docs/md/installing.md b/docs/md/installing.md index b2d512da7..c7b81696d 100644 --- a/docs/md/installing.md +++ b/docs/md/installing.md @@ -21,7 +21,7 @@ To install JFlex on Windows, follow these three steps: +--cup-maven\ (calculator example for cup and maven) +--interpreter\ (interpreter example for cup) +--java\ (Java lexer specification) - +--simple-maven\ (example scanner built with maven) + +--simple\ (example scanner with no parser) +--standalone-maven\ (a simple standalone scanner, built with maven) +--zero-reader\ (Readers that return 0 characters) diff --git a/docs/md/maven-plugin.md b/docs/md/maven-plugin.md index 9f332217c..3593f442b 100644 --- a/docs/md/maven-plugin.md +++ b/docs/md/maven-plugin.md @@ -7,6 +7,9 @@ generates a corresponding Java parser ### Usage +See [jflex-maven-plugin site](http://jflex-de.github.io/jflex-web/jflex-maven-plugin/plugin-info.html) +for more information. + #### Minimal configuration This configuration generates java code of a parser @@ -20,6 +23,7 @@ in sub-directories following the Java convention on package names. Update the `pom.xml` to add the plugin: + ``` @@ -28,7 +32,7 @@ Update the `pom.xml` to add the plugin: de.jflex jflex-maven-plugin - 1.7.0 + ${project.version} @@ -56,7 +60,7 @@ The generated Java code is placed into `src/main/java` instead of de.jflex jflex-maven-plugin - 1.6.0 + ${project.version} @@ -89,7 +93,7 @@ This generates the source for de.jflex jflex-maven-plugin - 1.6.0 + ${project.version} strict jlex @@ -132,7 +136,7 @@ More information in the [POM reference guide on plugins](http://maven.apache.org Which version of the plugin is best for you? - * jflex-maven-plugin-$VERSION depends on $VERSION + * jflex-maven-plugin-${project.version} depends on ${project.version} and requires Java 7 when you `mvn jflex:generate` * jflex-maven-plugin-1.7.0 depends on 1.7.0 diff --git a/jflex-maven-plugin/pom.xml b/jflex-maven-plugin/pom.xml index db1c66d60..3d1badcb5 100644 --- a/jflex-maven-plugin/pom.xml +++ b/jflex-maven-plugin/pom.xml @@ -77,6 +77,11 @@ + + com.google.code.findbugs + jsr305 + 3.0.2 + com.google.guava guava @@ -97,7 +102,7 @@ org.apache.maven.plugin-tools maven-plugin-annotations - 3.5 + 3.5.2 provided @@ -112,5 +117,34 @@ 3.3.0 test + + com.google.truth + truth + 0.42 + test + + + junit + junit + test + + + junit + junit + test + + + junit + junit + 4.12 + test + + + + + maven-plugin-plugin + + + diff --git a/jflex-maven-plugin/src/main/java/de/jflex/plugin/maven/ClassInfo.java b/jflex-maven-plugin/src/main/java/de/jflex/plugin/maven/ClassInfo.java index 67620211c..9aa1668ee 100644 --- a/jflex-maven-plugin/src/main/java/de/jflex/plugin/maven/ClassInfo.java +++ b/jflex-maven-plugin/src/main/java/de/jflex/plugin/maven/ClassInfo.java @@ -8,11 +8,37 @@ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ package de.jflex.plugin.maven; +import com.google.common.base.Strings; import java.io.File; +import java.util.Objects; +import javax.annotation.Nullable; class ClassInfo { - String className = null; - String packageName = null; + + final String className; + + /** dot-separated package name. Empty string for the default package. */ + final String packageName; + + ClassInfo(String className, @Nullable String packageName) { + this.className = className; + this.packageName = Strings.nullToEmpty(packageName); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof ClassInfo)) { + return false; + } + ClassInfo other = (ClassInfo) obj; + return Objects.equals(className, other.className) + && Objects.equals(packageName, other.packageName); + } + + @Override + public int hashCode() { + return Objects.hash(className, packageName); + } /** * Returns the (relative) path name of the java source code file that corresponds to the class. @@ -22,10 +48,7 @@ class ClassInfo { * @return Name of the java file. */ String getOutputFilename() { - String packageDir = ""; - if (packageName != null) { - packageDir += packageName.replace('.', File.separatorChar); - } + String packageDir = packageName.replace('.', File.separatorChar); if (packageDir.length() > 0) { packageDir += File.separatorChar; } diff --git a/jflex-maven-plugin/src/main/java/de/jflex/plugin/maven/JFlexMojo.java b/jflex-maven-plugin/src/main/java/de/jflex/plugin/maven/JFlexMojo.java index 8f80a187b..c81418b61 100644 --- a/jflex-maven-plugin/src/main/java/de/jflex/plugin/maven/JFlexMojo.java +++ b/jflex-maven-plugin/src/main/java/de/jflex/plugin/maven/JFlexMojo.java @@ -8,6 +8,8 @@ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ package de.jflex.plugin.maven; +import static com.google.common.base.Strings.isNullOrEmpty; + import com.google.common.base.Predicate; import com.google.common.collect.FluentIterable; import com.google.common.collect.ImmutableSet; @@ -18,7 +20,8 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import jflex.Main; +import java.util.Objects; +import jflex.LexGenerator; import jflex.Options; import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecutionException; @@ -47,7 +50,7 @@ public class JFlexMojo extends AbstractMojo { * List of grammar definitions to run the JFlex parser generator on. Each path may either specify * a single grammar file or a directory. Directories will be recursively scanned for files with * one of the following extensions: ".jflex", ".flex", ".jlex" or ".lex". By default, all files in - * src/main/jflex will be processed. + * {@code src/main/jflex} will be processed. * * @see #SRC_MAIN_JFLEX */ @@ -90,7 +93,7 @@ public class JFlexMojo extends AbstractMojo { @Parameter(defaultValue = "false") private boolean jlex; - /** The generation method to use for the scanner. The only valid value is pack. */ + /** The generation method to use for the scanner. The only valid value is {@code pack}. */ @Parameter(defaultValue = "pack") private String generationMethod = "pack"; // NOPMD @@ -189,16 +192,7 @@ private void parseLexFile(File lexFile) throws MojoFailureException, MojoExecuti assert lexFile.isAbsolute() : lexFile; getLog().debug("Generating Java code from " + lexFile.getName()); - ClassInfo classInfo; - try { - classInfo = LexSimpleAnalyzer.guessPackageAndClass(lexFile); - } catch (FileNotFoundException e) { - throw new MojoFailureException(e.getMessage(), e); - } catch (IOException e) { - classInfo = new ClassInfo(); - classInfo.className = LexSimpleAnalyzer.DEFAULT_NAME; - classInfo.packageName = null; // NOPMD - } + ClassInfo classInfo = findClassInfo(lexFile); checkParameters(lexFile); @@ -215,6 +209,7 @@ private void parseLexFile(File lexFile) throws MojoFailureException, MojoExecuti // set options. Very strange that JFlex expects this in a static way. Options.setDefaults(); Options.setDir(generatedFile.getParentFile()); + Options.setRootDirectory(project.getBasedir()); Options.dump = dump; Options.verbose = verbose; Options.unused_warning = unusedWarning; @@ -227,11 +222,11 @@ private void parseLexFile(File lexFile) throws MojoFailureException, MojoExecuti Options.no_minimize = !minimize; // NOPMD Options.no_backup = !backup; // NOPMD - if (!"pack".equals(generationMethod)) { + if (!Objects.equals("pack", generationMethod)) { throw new MojoExecutionException("Illegal generation method: " + generationMethod); } - if (!"".equals(encodingName)) { + if (!isNullOrEmpty(encodingName)) { try { Options.setEncoding(encodingName); } catch (Exception e) { @@ -240,13 +235,23 @@ private void parseLexFile(File lexFile) throws MojoFailureException, MojoExecuti } try { - Main.generate(lexFile); + LexGenerator.generate(lexFile); getLog().info(" generated " + generatedFile); } catch (Exception e) { throw new MojoExecutionException(e.getMessage(), e); } } + private ClassInfo findClassInfo(File lexFile) throws MojoFailureException { + try { + return LexSimpleAnalyzer.guessPackageAndClass(lexFile); + } catch (FileNotFoundException e) { + throw new MojoFailureException(e.getMessage(), e); + } catch (IOException e) { + return new ClassInfo(LexSimpleAnalyzer.DEFAULT_NAME, /*packageName=*/ ""); + } + } + /** * Check parameter lexFile. * diff --git a/jflex-maven-plugin/src/main/java/de/jflex/plugin/maven/LexSimpleAnalyzer.java b/jflex-maven-plugin/src/main/java/de/jflex/plugin/maven/LexSimpleAnalyzer.java index 1840ecd11..9e44bf2d4 100644 --- a/jflex-maven-plugin/src/main/java/de/jflex/plugin/maven/LexSimpleAnalyzer.java +++ b/jflex-maven-plugin/src/main/java/de/jflex/plugin/maven/LexSimpleAnalyzer.java @@ -9,17 +9,22 @@ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ package de.jflex.plugin.maven; +import com.google.common.io.Files; import java.io.File; import java.io.FileNotFoundException; -import java.io.FileReader; import java.io.IOException; import java.io.LineNumberReader; +import java.io.Reader; +import java.nio.charset.StandardCharsets; +import javax.annotation.Nullable; /** * @author Rafal Mantiuk (Rafal.Mantiuk@bellstream.pl) * @author Gerwin Klein (lsf@jflex.de) + * @author Régis Décamps */ class LexSimpleAnalyzer { + static final String DEFAULT_NAME = "Yylex"; /** @@ -31,51 +36,56 @@ class LexSimpleAnalyzer { * @throws IOException when an IO exception occurred while reading a file. */ static ClassInfo guessPackageAndClass(File lexFile) throws IOException { - assert lexFile.isAbsolute() : lexFile; + Reader lexFileReader = Files.newReader(lexFile, StandardCharsets.UTF_8); + return guessPackageAndClass(lexFileReader); + } - try (LineNumberReader reader = new LineNumberReader(new FileReader(lexFile))) { - ClassInfo classInfo = new ClassInfo(); - while (classInfo.className == null || classInfo.packageName == null) { + static ClassInfo guessPackageAndClass(Reader lexFileReader) throws IOException { + try (LineNumberReader reader = new LineNumberReader(lexFileReader)) { + String className = null; + String packageName = null; + while (className == null || packageName == null) { String line = reader.readLine(); if (line == null) { break; } - - guessPackage(classInfo, line); - guessClass(classInfo, line); + if (packageName == null) { + packageName = guessPackage(line); + } + if (className == null) { + className = guessClass(line); + } } - if (classInfo.className == null) { - classInfo.className = DEFAULT_NAME; + if (className == null) { + className = DEFAULT_NAME; } - return classInfo; + return new ClassInfo(className, packageName); } } - private static void guessClass(ClassInfo classInfo, String line) { - if (classInfo.className == null) { - int index = line.indexOf("%class"); - if (index >= 0) { - index += 6; - - classInfo.className = line.substring(index); - classInfo.className = classInfo.className.trim(); - } + @Nullable + private static String guessClass(String line) { + int index = line.indexOf("%class"); + if (index > -1) { + index += "%class".length(); + return line.substring(index).trim(); } + return null; } - private static void guessPackage(ClassInfo classInfo, String line) { - if (classInfo.packageName == null) { - int index = line.indexOf("package"); - if (index >= 0) { - index += 7; + @Nullable + private static String guessPackage(String line) { + int index = line.trim().indexOf("package"); + if (index == 0) { + index += "package".length(); - int end = line.indexOf(';', index); - if (end >= index) { - classInfo.packageName = line.substring(index, end); - classInfo.packageName = classInfo.packageName.trim(); - } + int end = line.indexOf(';', index); + if (end >= index) { + return line.substring(index, end).trim(); } } + + return null; } } diff --git a/jflex-maven-plugin/src/site/apt/changelog.apt b/jflex-maven-plugin/src/site/apt/changelog.apt deleted file mode 100644 index a461cf82d..000000000 --- a/jflex-maven-plugin/src/site/apt/changelog.apt +++ /dev/null @@ -1,107 +0,0 @@ - --------- - Changelog - --------- - -Latest version - -* Version 1.7.0 - - * Relies on JFlex 1.7.0 - - * Requires Java 7 - - * Requires Maven 3 - - * New option 'enconding' to set lexer spec encoding - -* Version 1.6.1 - - * Relies on JFlex 1.6.1 - - * New option `unusedWarning` that controls whether to warn about unused - macros or not. - - * New option `dump` that is more verbose than `verbose` - - * Option `verbose` set to same verbosity level as command line - - * Added m2e plugin configuration for Eclipse - - * Development moved to github - - -Recent versions - -* Version 1.6.0 - - * Relies on JFlex 1.6.0 - - * changed inputStreamCtor default to false - - -* Version 1.5.0 - - * Changed name from maven-jflex-plugin to jflex-maven-plugin - - * Switched license from GPL to BSD - - * Relies on JFlex 1.5.0 - - * Requires Java 5 - - * Changed package from org.codehaus.mojo.jlex to de.jflex.plugin.maven. - - -* Version 1.4.3-r1 - - * Java 1.3 bytecode - - * Relies on JFlex 1.4.3 - -* Version 1.4.2-r1 - - * Java 1.3 bytecode - - * Relies on JFlex 1.4.2 - - -* Version 0.3 - - * Java 1.4 bytecode - - * relies on JFlex 1.4.1 - - * new option staleMillis - - * more reports on site - - * code improvements - - -Alpha versions - -* Version 0.2 - - * Java 5 bytecode - - * new options added: generationMethod, minimize, backup - - * enhanced documentation - - * many code improvements - - ** use file comparison, prefer available functionality from File - class to avoid manual string twiddling - - ** fix testInit(): the generated file size depends on the platform - (because of how new lines are encoded) - - ** improve unit test with AbstractMojoTestCase - - * changed package from de.jflex.maven.plugin to org.codehaus.maven.plugin - -* Version 0.1 - - * Java 5 bytecode - - * Generates Java code from lex files, using JFlex 1.4.1 \ No newline at end of file diff --git a/jflex-maven-plugin/src/site/markdown/usage.md b/jflex-maven-plugin/src/site/markdown/usage.md new file mode 120000 index 000000000..e11538f65 --- /dev/null +++ b/jflex-maven-plugin/src/site/markdown/usage.md @@ -0,0 +1 @@ +../../../../docs/md/maven-plugin.md \ No newline at end of file diff --git a/jflex-maven-plugin/src/site/site.xml b/jflex-maven-plugin/src/site/site.xml new file mode 100644 index 000000000..316ffed4f --- /dev/null +++ b/jflex-maven-plugin/src/site/site.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/jflex-maven-plugin/src/test/java/de/jflex/plugin/maven/ClassInfoTest.java b/jflex-maven-plugin/src/test/java/de/jflex/plugin/maven/ClassInfoTest.java index d25704ace..770e6f465 100644 --- a/jflex-maven-plugin/src/test/java/de/jflex/plugin/maven/ClassInfoTest.java +++ b/jflex-maven-plugin/src/test/java/de/jflex/plugin/maven/ClassInfoTest.java @@ -8,7 +8,7 @@ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ package de.jflex.plugin.maven; -import static org.junit.Assert.assertEquals; +import static com.google.common.truth.Truth.assertThat; import java.io.File; import org.junit.Test; @@ -16,9 +16,13 @@ public class ClassInfoTest { @Test public void testGetOutputFilename() { - ClassInfo clazz = new ClassInfo(); - clazz.className = "Bar"; - clazz.packageName = "org.foo"; - assertEquals(new File("org/foo/Bar.java"), new File(clazz.getOutputFilename())); + ClassInfo clazz = new ClassInfo("Bar", "org.foo"); + assertThat(new File(clazz.getOutputFilename())).isEqualTo(new File("org/foo/Bar.java")); + } + + @Test + public void testGetOutputFilename_defaultPackage() { + ClassInfo clazz = new ClassInfo("Bar", null); + assertThat(new File(clazz.getOutputFilename())).isEqualTo(new File("Bar.java")); } } diff --git a/jflex-maven-plugin/src/test/java/de/jflex/plugin/maven/JFlexMojoTest.java b/jflex-maven-plugin/src/test/java/de/jflex/plugin/maven/JFlexMojoTest.java index c07e41f4a..65a0b5b3e 100644 --- a/jflex-maven-plugin/src/test/java/de/jflex/plugin/maven/JFlexMojoTest.java +++ b/jflex-maven-plugin/src/test/java/de/jflex/plugin/maven/JFlexMojoTest.java @@ -8,8 +8,8 @@ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ package de.jflex.plugin.maven; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth.assertWithMessage; import com.google.common.base.Predicate; import java.io.File; @@ -48,8 +48,9 @@ public void testTestResources() throws IOException { String actualResDir = testResources.getBasedir("single-file-test").getAbsolutePath(); String expectedSuffix = "/jflex-maven-plugin/target/test-projects/JFlexMojoTest_testTestResources_single-file-test"; - assertTrue( - actualResDir + " ends with " + expectedSuffix, actualResDir.endsWith(expectedSuffix)); + assertWithMessage(actualResDir + " ends with " + expectedSuffix) + .that(actualResDir.endsWith(expectedSuffix)) + .isTrue(); } /** Tests configuration with a single input file. */ @@ -59,7 +60,7 @@ public void testSingleFile() throws Exception { mojo.execute(); File produced = getExpectedOutputFile(mojo); - assertTrue("produced file is a file: " + produced, produced.isFile()); + assertWithMessage("produced file is a file: " + produced).that(produced.isFile()).isTrue(); long size = produced.length(); /* @@ -68,7 +69,9 @@ public void testSingleFile() throws Exception { * Windows platform ("\r\n") than on a Unix platform ("\n"). */ boolean correctSize = (size > 26624) && (size < 36696); - assertTrue("size of produced file between 26k and 36k. Actual is " + size, correctSize); + assertWithMessage("size of produced file between 26k and 36k. Actual is " + size) + .that(correctSize) + .isTrue(); } /** Tests configuration with a single input directory. */ @@ -78,7 +81,7 @@ public void testSingleDir() throws Exception { mojo.execute(); File produced = getExpectedOutputFile(mojo); - assertTrue("produced file is a file: " + produced, produced.isFile()); + assertWithMessage("produced file is a file: " + produced).that(produced.isFile()).isTrue(); } /** Tests configuration with a single input directory containing sub directories. */ @@ -88,16 +91,16 @@ public void testRecursion() throws Exception { mojo.execute(); File produced = getExpectedOutputFile(mojo); - assertTrue("produced file is a file: " + produced, produced.isFile()); + assertWithMessage("produced file is a file: " + produced).that(produced.isFile()).isTrue(); } @Test public void extensionPredicate() { Predicate predicate = new JFlexMojo.ExtensionPredicate("bar", "baz"); - assertTrue(predicate.apply(new File("/tmp/foo.bar"))); - assertTrue(predicate.apply(new File("/tmp/foo.baz"))); - assertFalse(predicate.apply(new File("/tmp/foo.bar.too"))); - assertFalse(predicate.apply(new File("/tmp/foo.blahblahbar"))); + assertThat(predicate.apply(new File("/tmp/foo.bar"))).isTrue(); + assertThat(predicate.apply(new File("/tmp/foo.baz"))).isTrue(); + assertThat(predicate.apply(new File("/tmp/foo.bar.too"))).isFalse(); + assertThat(predicate.apply(new File("/tmp/foo.blahblahbar"))).isFalse(); } /** diff --git a/jflex-maven-plugin/src/test/java/de/jflex/plugin/maven/LexSimpleAnalyzerTest.java b/jflex-maven-plugin/src/test/java/de/jflex/plugin/maven/LexSimpleAnalyzerTest.java new file mode 100644 index 000000000..561f2a669 --- /dev/null +++ b/jflex-maven-plugin/src/test/java/de/jflex/plugin/maven/LexSimpleAnalyzerTest.java @@ -0,0 +1,87 @@ +package de.jflex.plugin.maven; + +import static com.google.common.truth.Truth.assertThat; + +import java.io.IOException; +import java.io.StringReader; +import org.junit.Test; + +/** Test for {@link LexSimpleAnalyzer}. */ +public class LexSimpleAnalyzerTest { + + @Test + public void guessPackageAndClass_givenClass_defaultPackage() throws Exception { + String lex = + "\n" + + "%%\n" + + "\n" + + "%public\n" + + "%class Foo\n" + + "\n" + + "%apiprivate\n" + + "%int\n" + + "\n" + + "%%\n" + + "\n" + + "[^] { /* no action */ }\n"; + assertThat(guessPackageAndClass(lex)).isEqualTo(new ClassInfo("Foo", null)); + } + + @Test + public void guessPackageAndClass_defaultClass_defaultPackage() throws Exception { + String lex = + "\n" + + "%%\n" + + "\n" + + "%public\n" + + "\n" + + "%%\n" + + "\n" + + "^\"hello\"$ { System.out.println(\"hello\"); }\n" + + "\n"; + assertThat(guessPackageAndClass(lex)).isEqualTo(new ClassInfo("Yylex", null)); + } + + @Test + public void guessPackageAndClass_defaultClass_hintPackage() throws Exception { + String lex = + "\n" + + "package org.example;\n" + + "\n" + + "import java.io.File;\n" + + "\n" + + "%%\n" + + "\n" + + "%final\n" + + "%public\n" + + "\n"; + assertThat(guessPackageAndClass(lex)).isEqualTo(new ClassInfo("Yylex", "org.example")); + } + + /** + * Tests that a random "package" string doesn't mislead JFlex in finding a package name. + * + *

See issue #104. + */ + @Test + public void guessPackageAndClass_defaultClass_misleadingPackage() throws Exception { + String lex = + "\n" + + "%%\n" + + "\n" + + "%public\n" + + "\n" + + "%%\n" + + "\n" + + " {" + + " \"package\" { return symbol(PACKAGE); }\n" + + " \"private\" { return symbol(PRIVATE); }" + + "}\n" + + "\n"; + assertThat(guessPackageAndClass(lex)).isEqualTo(new ClassInfo("Yylex", null)); + } + + private ClassInfo guessPackageAndClass(String lex) throws IOException { + return LexSimpleAnalyzer.guessPackageAndClass(new StringReader(lex)); + } +} diff --git a/jflex-unicode-maven-plugin/README.md b/jflex-unicode-maven-plugin/README.md new file mode 100644 index 000000000..f12031c71 --- /dev/null +++ b/jflex-unicode-maven-plugin/README.md @@ -0,0 +1,8 @@ +# Unicode maven plugin + +If you are not a JFlex developer, you don't need this plugin. + +This plugin fetches Unicode from unicode.org and generate their corresponding Java sources. + +Because this process takes time, we have actually checked-in the code in [jflex.unicode.data](..//jflex/src/main/java/jflex/unicode/data). + diff --git a/jflex-unicode-maven-plugin/src/main/java/jflex/DataFileType.java b/jflex-unicode-maven-plugin/src/main/java/jflex/DataFileType.java index 4eddea2c5..c44d7f550 100644 --- a/jflex-unicode-maven-plugin/src/main/java/jflex/DataFileType.java +++ b/jflex-unicode-maven-plugin/src/main/java/jflex/DataFileType.java @@ -5,56 +5,62 @@ import java.io.Reader; import java.net.MalformedURLException; import java.net.URL; +import java.util.Objects; import java.util.regex.Matcher; import java.util.regex.Pattern; public enum DataFileType { PROPERTY_ALIASES("PropertyAliases") { public void scan(URL url, UnicodeVersion version) throws IOException { - Reader reader = new InputStreamReader(url.openStream(), "UTF-8"); - PropertyAliasesScanner scanner = new PropertyAliasesScanner(reader, version); - scanner.scan(); + try (Reader reader = new InputStreamReader(url.openStream(), "UTF-8")) { + PropertyAliasesScanner scanner = new PropertyAliasesScanner(reader, version); + scanner.scan(); + } } }, PROPERTY_VALUE_ALIASES("PropertyValueAliases") { public void scan(URL url, UnicodeVersion version) throws IOException { - Reader reader = new InputStreamReader(url.openStream(), "UTF-8"); - PropertyValueAliasesScanner scanner = new PropertyValueAliasesScanner(reader, version); - scanner.scan(); + try (Reader reader = new InputStreamReader(url.openStream(), "UTF-8")) { + PropertyValueAliasesScanner scanner = new PropertyValueAliasesScanner(reader, version); + scanner.scan(); + } } }, UNICODE_DATA("UnicodeData") { public void scan(URL url, UnicodeVersion version) throws IOException { - Reader reader = new InputStreamReader(url.openStream(), "UTF-8"); - UnicodeDataScanner scanner = new UnicodeDataScanner(reader, version); - scanner.scan(); + try (Reader reader = new InputStreamReader(url.openStream(), "UTF-8")) { + UnicodeDataScanner scanner = new UnicodeDataScanner(reader, version); + scanner.scan(); + } } }, PROPLIST("PropList") { public void scan(URL url, UnicodeVersion version) throws IOException { - Reader reader = new InputStreamReader(url.openStream(), "UTF-8"); - // Before Unicode 3.1, PropList-X.X.X.txt used a different format. - // Before Unicode 2.0, PropList-X.X.X.txt did not exist. - if (version.majorMinorVersion.equals("2.0") - || version.majorMinorVersion.equals("2.1") - || version.majorMinorVersion.equals("3.0")) { - ArchaicPropListScanner scanner = new ArchaicPropListScanner(reader, version); - scanner.scan(); - } else { - BinaryPropertiesFileScanner scanner = new BinaryPropertiesFileScanner(reader, version); - scanner.scan(); + try (Reader reader = new InputStreamReader(url.openStream(), "UTF-8")) { + // Before Unicode 3.1, PropList-X.X.X.txt used a different format. + // Before Unicode 2.0, PropList-X.X.X.txt did not exist. + if (Objects.equals(version.majorMinorVersion, "2.0") + || Objects.equals(version.majorMinorVersion, "2.1") + || Objects.equals(version.majorMinorVersion, "3.0")) { + ArchaicPropListScanner scanner = new ArchaicPropListScanner(reader, version); + scanner.scan(); + } else { + BinaryPropertiesFileScanner scanner = new BinaryPropertiesFileScanner(reader, version); + scanner.scan(); + } } } }, DERIVED_CORE_PROPERTIES("DerivedCoreProperties") { public void scan(URL url, UnicodeVersion version) throws IOException { - Reader reader = new InputStreamReader(url.openStream(), "UTF-8"); - BinaryPropertiesFileScanner scanner = new BinaryPropertiesFileScanner(reader, version); - scanner.scan(); + try (Reader reader = new InputStreamReader(url.openStream(), "UTF-8")) { + BinaryPropertiesFileScanner scanner = new BinaryPropertiesFileScanner(reader, version); + scanner.scan(); + } } }, @@ -64,95 +70,103 @@ public void scan(URL url, UnicodeVersion version) throws IOException { // From Unicode 5.0 onward, the default Script property value is "Unknown". // Prior to Unicode 3.1, Scripts(-X.X.X).txt did not exist. String defaultPropertyValue = "Unknown"; - if (version.majorMinorVersion.equals("3.1") - || version.majorMinorVersion.equals("3.2") - || version.majorMinorVersion.equals("4.0") - || version.majorMinorVersion.equals("4.1")) { + if (Objects.equals(version.majorMinorVersion, "3.1") + || Objects.equals(version.majorMinorVersion, "3.2") + || Objects.equals(version.majorMinorVersion, "4.0") + || Objects.equals(version.majorMinorVersion, "4.1")) { defaultPropertyValue = "Common"; } - Reader reader = new InputStreamReader(url.openStream(), "UTF-8"); - EnumeratedPropertyFileScanner scanner = - new EnumeratedPropertyFileScanner(reader, version, "Script", defaultPropertyValue); - scanner.scan(); + try (Reader reader = new InputStreamReader(url.openStream(), "UTF-8")) { + EnumeratedPropertyFileScanner scanner = + new EnumeratedPropertyFileScanner(reader, version, "Script", defaultPropertyValue); + scanner.scan(); + } } }, // SCRIPT_EXTENSIONS must follow SCRIPTS SCRIPT_EXTENSIONS("ScriptExtensions") { public void scan(URL url, UnicodeVersion version) throws IOException { - Reader reader = new InputStreamReader(url.openStream(), "UTF-8"); - ScriptExtensionsScanner scanner = - new ScriptExtensionsScanner(reader, version, "Script_Extensions"); - scanner.scan(); + try (Reader reader = new InputStreamReader(url.openStream(), "UTF-8")) { + ScriptExtensionsScanner scanner = + new ScriptExtensionsScanner(reader, version, "Script_Extensions"); + scanner.scan(); + } } }, BLOCKS("Blocks") { public void scan(URL url, UnicodeVersion version) throws IOException { - Reader reader = new InputStreamReader(url.openStream(), "UTF-8"); - // Before Unicode 3.1, Blocks-X.txt used a different format. - // Before Unicode 2.0, Blocks-X.txt did not exist. - if (version.majorMinorVersion.equals("2.0") - || version.majorMinorVersion.equals("2.1") - || version.majorMinorVersion.equals("3.0")) { - ArchaicBlocksScanner scanner = new ArchaicBlocksScanner(reader, version); - scanner.scan(); - } else { - EnumeratedPropertyFileScanner scanner = - new EnumeratedPropertyFileScanner(reader, version, "Block", "No_Block"); - scanner.scan(); + try (Reader reader = new InputStreamReader(url.openStream(), "UTF-8")) { + // Before Unicode 3.1, Blocks-X.txt used a different format. + // Before Unicode 2.0, Blocks-X.txt did not exist. + if (Objects.equals(version.majorMinorVersion, "2.0") + || Objects.equals(version.majorMinorVersion, "2.1") + || Objects.equals(version.majorMinorVersion, "3.0")) { + ArchaicBlocksScanner scanner = new ArchaicBlocksScanner(reader, version); + scanner.scan(); + } else { + EnumeratedPropertyFileScanner scanner = + new EnumeratedPropertyFileScanner(reader, version, "Block", "No_Block"); + scanner.scan(); + } } } }, LINE_BREAK("LineBreak") { public void scan(URL url, UnicodeVersion version) throws IOException { - Reader reader = new InputStreamReader(url.openStream(), "UTF-8"); - // In Unicode 3.0, LineBreak-X.txt used a different format. - // Before Unicode 3.0, LineBreak-X.txt did not exist. - if (version.majorMinorVersion.equals("3.0")) { - ArchaicLineBreakScanner scanner = new ArchaicLineBreakScanner(reader, version); - scanner.scan(); - } else { - EnumeratedPropertyFileScanner scanner = - new EnumeratedPropertyFileScanner(reader, version, "Line_Break", "XX"); - scanner.scan(); + try (Reader reader = new InputStreamReader(url.openStream(), "UTF-8")) { + // In Unicode 3.0, LineBreak-X.txt used a different format. + // Before Unicode 3.0, LineBreak-X.txt did not exist. + if (Objects.equals(version.majorMinorVersion, "3.0")) { + ArchaicLineBreakScanner scanner = new ArchaicLineBreakScanner(reader, version); + scanner.scan(); + } else { + EnumeratedPropertyFileScanner scanner = + new EnumeratedPropertyFileScanner(reader, version, "Line_Break", "XX"); + scanner.scan(); + } } } }, GRAPHEME_BREAK_PROPERTY("GraphemeBreakProperty") { public void scan(URL url, UnicodeVersion version) throws IOException { - Reader reader = new InputStreamReader(url.openStream(), "UTF-8"); - EnumeratedPropertyFileScanner scanner = - new EnumeratedPropertyFileScanner(reader, version, "Grapheme_Cluster_Break", "Other"); - scanner.scan(); + try (Reader reader = new InputStreamReader(url.openStream(), "UTF-8")) { + EnumeratedPropertyFileScanner scanner = + new EnumeratedPropertyFileScanner(reader, version, "Grapheme_Cluster_Break", "Other"); + scanner.scan(); + } } }, SENTENCE_BREAK_PROPERTY("SentenceBreakProperty") { public void scan(URL url, UnicodeVersion version) throws IOException { - Reader reader = new InputStreamReader(url.openStream(), "UTF-8"); - EnumeratedPropertyFileScanner scanner = - new EnumeratedPropertyFileScanner(reader, version, "Sentence_Break", "Other"); - scanner.scan(); + try (Reader reader = new InputStreamReader(url.openStream(), "UTF-8")) { + EnumeratedPropertyFileScanner scanner = + new EnumeratedPropertyFileScanner(reader, version, "Sentence_Break", "Other"); + scanner.scan(); + } } }, WORD_BREAK_PROPERTY("WordBreakProperty") { public void scan(URL url, UnicodeVersion version) throws IOException { - Reader reader = new InputStreamReader(url.openStream(), "UTF-8"); - EnumeratedPropertyFileScanner scanner = - new EnumeratedPropertyFileScanner(reader, version, "Word_Break", "Other"); - scanner.scan(); + try (Reader reader = new InputStreamReader(url.openStream(), "UTF-8")) { + EnumeratedPropertyFileScanner scanner = + new EnumeratedPropertyFileScanner(reader, version, "Word_Break", "Other"); + scanner.scan(); + } } }, DERIVED_AGE("DerivedAge") { public void scan(URL url, UnicodeVersion version) throws IOException { - Reader reader = new InputStreamReader(url.openStream(), "UTF-8"); - DerivedAgeScanner scanner = new DerivedAgeScanner(reader, version); - scanner.scan(); + try (Reader reader = new InputStreamReader(url.openStream(), "UTF-8")) { + DerivedAgeScanner scanner = new DerivedAgeScanner(reader, version); + scanner.scan(); + } } /** * Always return the URL for the latest Unicode version's DerivedAge.txt, except for Unicode diff --git a/jflex-unicode-maven-plugin/src/main/java/jflex/NamedRange.java b/jflex-unicode-maven-plugin/src/main/java/jflex/NamedRange.java index 567b70db4..dea4f86be 100644 --- a/jflex-unicode-maven-plugin/src/main/java/jflex/NamedRange.java +++ b/jflex-unicode-maven-plugin/src/main/java/jflex/NamedRange.java @@ -1,7 +1,10 @@ package jflex; +import java.util.Objects; + /** Internal-use class to represent code point intervals. */ public class NamedRange implements Comparable { + int start; int end; String name; @@ -17,11 +20,27 @@ public class NamedRange implements Comparable { this.name = name; } - public boolean equals(NamedRange other) { - return null != other - && (start == other.start - && end == other.end - && ((null == name && null == other.name) || (null != name && name.equals(other.name)))); + @Override + public boolean equals(Object obj) { + if (!(obj instanceof NamedRange)) { + return false; + } + NamedRange other = (NamedRange) obj; + return start == other.start && end == other.end && Objects.equals(name, other.name); + } + + @Override + public int hashCode() { + int h = 1; + h *= 1_000_003; + h ^= start; + h *= 1_000_003; + h ^= end; + if (name != null) { + h *= 1_000_003; + h ^= name.hashCode(); + } + return h; } public int compareTo(NamedRange other) { diff --git a/jflex-unicode-maven-plugin/src/main/java/jflex/UnicodePropertiesSkeleton.java b/jflex-unicode-maven-plugin/src/main/java/jflex/UnicodePropertiesSkeleton.java index 8e768e51c..937e44501 100644 --- a/jflex-unicode-maven-plugin/src/main/java/jflex/UnicodePropertiesSkeleton.java +++ b/jflex-unicode-maven-plugin/src/main/java/jflex/UnicodePropertiesSkeleton.java @@ -22,7 +22,7 @@ * portion of generated code between every two parts of skeleton code. * *

There is a static part (the skeleton code) and state based iterator part to this class. The - * iterator part is used to emit consecutive skeleton sections to some StringBuilder. + * iterator part is used to emit consecutive skeleton sections to some {@code StringBuilder}. * *

Copied from the version used for scanner generation. */ diff --git a/jflex-unicode-maven-plugin/src/main/java/jflex/UnicodeVersion.java b/jflex-unicode-maven-plugin/src/main/java/jflex/UnicodeVersion.java index f81f1d164..c56dcbbd3 100644 --- a/jflex-unicode-maven-plugin/src/main/java/jflex/UnicodeVersion.java +++ b/jflex-unicode-maven-plugin/src/main/java/jflex/UnicodeVersion.java @@ -23,6 +23,7 @@ import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.SortedMap; import java.util.SortedSet; @@ -302,7 +303,7 @@ void addInterval(String propName, int startCodePoint, int endCodePoint) { // U+4E00 is listed in UnicodeData-1.1.5.txt as having the "Lo" property // value, as are the previous code points, so to include // [ U+4E00 - U+9FFF ], this interval should be extended to U+9FFF. - if (range.end == 0x4E00 && majorMinorVersion.equals("1.1")) { + if (range.end == 0x4E00 && Objects.equals(majorMinorVersion, "1.1")) { range.end = 0x9FFF; } intervals.add(new NamedRangeSet(range)); @@ -355,7 +356,8 @@ void addInterval(String propName, String propValue, int startCodePoint, int endC List ranges = removeSurrogates(startCodePoint, endCodePoint); if (!ranges.isEmpty()) { String canonicalValue = propName + '=' + propValue; - if (propName.equals(NORMALIZED_GENERAL_CATEGORY) || propName.equals(NORMALIZED_SCRIPT)) { + if (Objects.equals(propName, NORMALIZED_GENERAL_CATEGORY) + || Objects.equals(propName, NORMALIZED_SCRIPT)) { canonicalValue = propValue; } NamedRangeSet intervals = propertyValueIntervals.get(canonicalValue); @@ -374,7 +376,9 @@ void addInterval(String propName, String propValue, int startCodePoint, int endC // FE70; FEFF; Arabic Presentation Forms-B // ... // FEFF; FEFF; Specials - if (range.start == 0xFE70 && range.end == 0xFEFF && majorMinorVersion.equals("2.0")) { + if (range.start == 0xFE70 + && range.end == 0xFEFF + && Objects.equals(majorMinorVersion, "2.0")) { range.end = 0xFEFE; } intervals.add(new NamedRangeSet(range)); @@ -388,7 +392,7 @@ void addInterval(String propName, String propValue, int startCodePoint, int endC // Initial letters of two-letter General Category property values // should be put on the used property values list - if (propName.equals(NORMALIZED_GENERAL_CATEGORY) && propValue.length() == 2) { + if (Objects.equals(propName, NORMALIZED_GENERAL_CATEGORY) && propValue.length() == 2) { String firstLetter = propValue.substring(0, 1); usedValues.add(firstLetter); } @@ -493,7 +497,7 @@ SortedMap getUsedPropertyValueAliases() { SortedMap usedPropertyValueAliases = new TreeMap<>(); for (String binaryProperty : usedBinaryProperties) { for (String nameAlias : getPropertyAliases(binaryProperty)) { - if (!nameAlias.equals(binaryProperty)) { + if (!Objects.equals(nameAlias, binaryProperty)) { usedPropertyValueAliases.put(nameAlias, binaryProperty); } } @@ -509,10 +513,11 @@ SortedMap getUsedPropertyValueAliases() { String canonicalValue = propName + '=' + propValue; // Add value-only aliases for General Category and Script properties. - if (propName.equals(NORMALIZED_SCRIPT) || propName.equals(NORMALIZED_GENERAL_CATEGORY)) { + if (Objects.equals(propName, NORMALIZED_SCRIPT) + || Objects.equals(propName, NORMALIZED_GENERAL_CATEGORY)) { canonicalValue = propValue; for (String valueAlias : getPropertyValueAliases(propName, propValue)) { - if (!valueAlias.equals(propValue)) { + if (!Objects.equals(valueAlias, propValue)) { usedPropertyValueAliases.put(valueAlias, propValue); } } @@ -523,9 +528,10 @@ SortedMap getUsedPropertyValueAliases() { // all possible alias combinations, exclude the one that is the same // as the full property name + full property value, unless the // property is General Category or Script. - if (propName.equals(NORMALIZED_SCRIPT) - || propName.equals(NORMALIZED_GENERAL_CATEGORY) - || !(nameAlias.equals(propName) && valueAlias.equals(propValue))) { + if (Objects.equals(propName, NORMALIZED_SCRIPT) + || Objects.equals(propName, NORMALIZED_GENERAL_CATEGORY) + || !(Objects.equals(nameAlias, propName) + && Objects.equals(valueAlias, propValue))) { String alias = nameAlias + '=' + valueAlias; usedPropertyValueAliases.put(alias, canonicalValue); } diff --git a/jflex/.gitignore b/jflex/.gitignore deleted file mode 100644 index c3ebe0a51..000000000 --- a/jflex/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -# Generated by ant -build - diff --git a/jflex/BUILD b/jflex/BUILD new file mode 100644 index 000000000..27ab922c3 --- /dev/null +++ b/jflex/BUILD @@ -0,0 +1,43 @@ +package(default_visibility = ["//visibility:public"]) + +load("//cup:cup.bzl", "cup") +load("@jflex_rules//jflex:jflex.bzl", "jflex") + +java_binary( + name = "jflex_bin", + main_class = "jflex.Main", + runtime_deps = [ + ":jflex", + "//cup:cup_runtime", + ], +) + +java_library( + name = "jflex", + srcs = glob(["src/main/java/**/*.java"]) + [ + ":gen_parser", + ":gen_scanner", + ], + resources = glob(["src/main/resources/**"]), + visibility = ["//visibility:public"], + deps = [ + "//cup", + "//third_party/org/apache/ant", + ], +) + +cup( + name = "gen_parser", + src = "src/main/cup/LexParse.cup", + interface = True, + parser = "LexParse", + symbols = "sym", +) + +# Use bootstrap version defined in @jflex_rules +jflex( + name = "gen_scanner", + srcs = ["src/main/jflex/LexScan.flex"], + outputs = ["LexScan.java"], + skeleton = "src/main/jflex/skeleton.nested", +) diff --git a/jflex/README.md b/jflex/README.md index b5262df9d..5bc26debb 100644 --- a/jflex/README.md +++ b/jflex/README.md @@ -3,33 +3,27 @@ This directory contains JFlex, a fast scanner generator for Java. To run JFlex, run `bin/jflex` from the command line or double click on the -JFLex jar file in the `lib/` directory. You need JDK 1.7 installed and set up. +JFLex jar file in the `lib/` directory. See the manual in `doc/` or the website at for more information and for how to get started. -## Contents ## +## Contents - bin/ command line start scripts - doc/ manual - examples/ some example scanners - lib/ JFlex jar file, syntax highlighting files - src/ JFLex sources - - pom.xml Maven project object model, useful commands: - mvn compile build and compile JFlex - mvn test run unit tests - mvn package generate the JFlex jar - - build.xml Ant build file, useful commands: - ant gettools get tools to run Ant build - ant compile build and compile JFlex - ant test run unit tests - ant jar generate the JFlex jar + ├── build.xml script to build with ant + ├── changelog.md summary of the changes + ├── pom.xml project object model to build with Maven + ├── README.md this file + ├── bin command line start scripts + ├── [build] build directory if you used ant + ├── doc user manual + ├── examples example scanners and parsers + ├── lib syntax highlighting files ; also JFlex jar in binary distribution + ├── src JFLex sources + └── target - -## Dependencies ## +## Dependencies * To run JFlex, you need at least JDK 1.7. * To build JFlex, you need JDK 1.7+ and Maven 3. diff --git a/jflex/bin/jflex.bat b/jflex/bin/jflex.bat index 70bc776fe..50d848467 100755 --- a/jflex/bin/jflex.bat +++ b/jflex/bin/jflex.bat @@ -1,8 +1,8 @@ @echo off -REM Please adjust JFLEX_HOME to suit your needs +REM Please adjust JFLEX_HOME and JFLEX_VERSION to suit your needs REM (please do not add a trailing backslash) -set JFLEX_HOME=C:\JFLEX -set JFLEX_VERSION=1.7.1-SNAPSHOT +if not defined JFLEX_HOME set JFLEX_HOME=C:\JFLEX +if not defined JFLEX_VERSION set JFLEX_VERSION=1.7.1-SNAPSHOT -java -Xmx128m -jar "%JFLEX_HOME%"\lib\jflex-full-%JFLEX_VERSION%.jar %* +java -Xmx128m -jar "%JFLEX_HOME%\lib\jflex-full-%JFLEX_VERSION%.jar" %* diff --git a/jflex/build.xml b/jflex/build.xml deleted file mode 100644 index 12ab2fb05..000000000 --- a/jflex/build.xml +++ /dev/null @@ -1,159 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/jflex/examples/.gitignore b/jflex/examples/.gitignore index cdb8ba5c2..b03ceed63 100644 --- a/jflex/examples/.gitignore +++ b/jflex/examples/.gitignore @@ -1,10 +1,5 @@ -*/output.txt -interpreter/Yylex.java -java/Scanner.java -java/UnicodeEscapes.java -java/lexer-output.txt -cup/Lexer.java -byaccj/Yylex.java -zero-reader/ZeroLexer.java -*/parser.java -*/sym.java +antbuild +target +out +cup_runtime.jar +jflex.jar diff --git a/jflex/examples/README.md b/jflex/examples/README.md new file mode 100644 index 000000000..63bcc486a --- /dev/null +++ b/jflex/examples/README.md @@ -0,0 +1,94 @@ +# JFlex examples + +## The Examples + +* **byaccj**: + integration between JFlex and byacc/j on a simple parser for an arithmetic calculator +* **cup-interpreter**: + integration between JFlex and CUP on a complex interpreter +* **cup-java**: + integration between JFlex and CUP on a a Java parser +* **cup-lcalc**: + integration between JFlex and CUP on a simple parser for a arithmetic calculator +* **simple**: + a simple lexer with no parser integration +* **standalone**: + an "hello world" parser for which the `main` is generated automatically +* **zero-reader**: + This example shows how to work with input Readers that sometimes return 0 + characters. + +Every example also provides its own `README.md` with more context. + + +## Build systems + +All examples (try to) support multiple build systems: +- [Maven](https://maven.apache.org/). +- [ant](https://ant.apache.org/). +- [bazel](https://bazel.build/). +- [make](https://www.gnu.org/software/make). + +All examples follow the Maven layout, in particular: +- `src` contains the source files + - `src/main` contains the source files for the actual programm + - `src/main/java` contains the Java sources + - `src/main/jflex` contains the flex definitions that should be used by JFlex + to generate java code + - `src/main/cup` contains the CUP definitions that should be used by CUP + - `src/test` contains the source files for tests + - `src/test/data` contains an input file for the scanner; as well as a _golden file_ + of the expected output for this input. + + +### Maven + +When the example can be build with Maven, there is a `pom.xml`. + +Please use `mvn package` to +- generate the Java source from flex and cup definitions +- compile all Java source +- run unit tests + +Build artifacts are in `target` (in subdirectories depending on their nature). + +In the end run the compiled lexer with: +`java -jar target/` _BUILD-ARTEFACT.jar_ `` + + +### ant + +When the example can be build with Maven, there is a `build.xml`. + +We place build artifacts in the `antbuild` directory. + +Also, we consistently use: +- `ant` _default action_ for **compile** +- `ant compile` to generate the source code form flex and cup definitions + and compile all Java code +- `ant run` to run the lexer on a sample input +- `ant test` to run the lexer on the sample input and check it produces + the expected output. + + +### Bazel + +We test the examples with Bazel. + +In order to use the Skylark "jflex()" rule and see example, please see +[jflex-de/bazel_rules](https://github.com/jflex-de/bazel_rules). + + +### GNU make + +When the example can be build with make, there is a `Makefile`. + +We place build artifacts in the `out` directory. + +Also, we consistently use: +- `make` _default action_ for **compile** +- `make compile` to generate the source code form flex and cup definitions + and compile all Java code +- `make run` to run the lexer on a sample input +- `make test` to run the lexer on the sample input and check it produces + the expected output. diff --git a/jflex/examples/byaccj/Makefile b/jflex/examples/byaccj/Makefile index 3312221b7..8597b57ec 100644 --- a/jflex/examples/byaccj/Makefile +++ b/jflex/examples/byaccj/Makefile @@ -1,27 +1,23 @@ -# only works with the Java extension of yacc: -# byacc/j from http://troi.lincom-asg.com/~rjamison/byacc/ +# Be sure to use yacc/j ; not unix yacc nor bison. +BYACCJ = yaccj -J +ROOT = ../../.. +JFLEX = $(ROOT)/jflex/bin/jflex -JFLEX = ../../bin/jflex -BYACCJ = yacc -J -JAVAC = javac +run: all + java -cp out Parser -# targets: +all: build -all: Parser.class - -run: Parser.class - java Parser - -build: clean Parser.class +build: Parser.class clean: - rm -f *~ *.class *.java + rm -rf out Parser.class: Yylex.java Parser.java - $(JAVAC) Parser.java + javac -d out out/*.java -Yylex.java: calc.flex - $(JFLEX) calc.flex +Yylex.java: src/main/jflex/calc.flex + $(JFLEX) -d out $< -Parser.java: calc.y - $(BYACCJ) calc.y +Parser.java: src/main/yacc/calc.y + $(BYACCJ) $< ; mv Parser.java ParserVal.java out diff --git a/jflex/examples/byaccj/README.md b/jflex/examples/byaccj/README.md index 4e04bf92a..1ba0d3b59 100644 --- a/jflex/examples/byaccj/README.md +++ b/jflex/examples/byaccj/README.md @@ -1,30 +1,32 @@ JFlex with Yacc =============== -This directory contains an interoperability example for BYacc/J and JFlex. +This directory contains an interoperability example for [BYacc/J][byaccj] and JFlex. This example implements a small calculator. -## Build and run +## Build, run, test + +### Build with Make Use the Makefile to generate the lexer and parser. ``` -make +make all ``` The example can then be started with ``` -java Parser +make run ``` ## Files -* `calc.flex` +* `src/main/jflex/calc.flex` JFlex specification for the lexical part of the arithmetic expressions. -* `calc.y` +* `src/main/yacc/calc.y` BYacc/J specification and main program for the calculator. diff --git a/jflex/examples/byaccj/calc.flex b/jflex/examples/byaccj/src/main/jflex/calc.flex similarity index 100% rename from jflex/examples/byaccj/calc.flex rename to jflex/examples/byaccj/src/main/jflex/calc.flex diff --git a/jflex/examples/byaccj/calc.y b/jflex/examples/byaccj/src/main/yacc/calc.y similarity index 100% rename from jflex/examples/byaccj/calc.y rename to jflex/examples/byaccj/src/main/yacc/calc.y diff --git a/jflex/examples/cup-interpreter/Makefile b/jflex/examples/cup-interpreter/Makefile new file mode 100644 index 000000000..debf5e2c8 --- /dev/null +++ b/jflex/examples/cup-interpreter/Makefile @@ -0,0 +1,40 @@ +# uses JFlex >= 1.3.2, and CUP >= 0.10j +# +# targets: +# +# make all (or just: make) +# generates lexer, and parser, and compiles all *.java files +# +# make test +# starts the program on a test example +# + +ROOT = ../../.. +JFLEX = $(ROOT)/jflex/bin/jflex +CUPJAR = $(ROOT)/cup/cup/java-cup-11b.jar +CUP = java -jar $(CUPJAR) + +all: clean compile + +compile: gen_cup gen_jflex src/main/java/*.java + javac -cp $(CUPJAR):out -d out src/main/java/*.java out/genfiles/*.java + +# writes parser.java sym.java +gen_jflex: src/main/jflex/scanner.flex outdir + $(JFLEX) -d out/genfiles $< + +gen_cup: src/main/cup/parser.cup outdir + $(CUP) -interface < $< + mv parser.java sym.java out/genfiles + +output: compile src/test/data/example.as outdir + java -cp $(CUPJAR):out Main < src/test/data/example.as > out/output.txt + +test: output + @(diff out/output.txt src/test/data/output.good && echo "Test OK!") || (echo "Test FAILED"; exit 1) + +clean: + rm -rf out + +outdir: + mkdir -p out/genfiles diff --git a/jflex/examples/interpreter/README.md b/jflex/examples/cup-interpreter/README.md similarity index 73% rename from jflex/examples/interpreter/README.md rename to jflex/examples/cup-interpreter/README.md index 2db90695f..ca3b5a49b 100644 --- a/jflex/examples/interpreter/README.md +++ b/jflex/examples/cup-interpreter/README.md @@ -4,6 +4,28 @@ FJlex complex example (with CUP) This directory contains an interpreter for a small functional programming language (called "AS"). It uses JFlex and CUP. +## Build, run, test + +### Using Maven + + ../../../mvnw package + +To run the interpreter on a sample input. + + java -jar target/cup-interpreter-full-1.0.jar src/test/data/example.as + +You can expect `src/test/data/output.good`. + +### Using ant + + ant compile + ant run + +### Using make + + make test + java -cp java -cp ../../../cup/cup/java-cup-11b.jar:out src/test/data/example.as + ## Files - `Main.java` is the main program. It reads an AS program from diff --git a/jflex/examples/cup-interpreter/build.xml b/jflex/examples/cup-interpreter/build.xml new file mode 100644 index 000000000..79de2a52e --- /dev/null +++ b/jflex/examples/cup-interpreter/build.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/jflex/examples/cup-interpreter/pom.xml b/jflex/examples/cup-interpreter/pom.xml new file mode 100644 index 000000000..facef23ce --- /dev/null +++ b/jflex/examples/cup-interpreter/pom.xml @@ -0,0 +1,105 @@ + + + 4.0.0 + de.flex.examples + cup-interpreter + 1.0 + A parser for the AS language + An interpreter for a small functional + programming language (called "AS"). + It uses JFlex and CUP. + + + de.jflex + cup_runtime + 11b + + + junit + junit + 3.8.2 + test + + + com.google.truth + truth + 0.36 + test + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.0 + + 1.6 + 1.6 + + + + de.jflex + jflex-maven-plugin + 1.7.1-SNAPSHOT + + + + generate + + + + + + + + de.jflex + cup-maven-plugin + 1.0 + + + + generate + + + true + + + + + + + + + + org.apache.maven.plugins + maven-shade-plugin + + + package + + shade + + + ${project.artifactId}-full-${project.version} + + + Main + + + + + de.jflex:cup_runtime + + + + + + + + + + UTF-8 + + diff --git a/jflex/examples/interpreter/parser.cup b/jflex/examples/cup-interpreter/src/main/cup/parser.cup similarity index 100% rename from jflex/examples/interpreter/parser.cup rename to jflex/examples/cup-interpreter/src/main/cup/parser.cup diff --git a/jflex/examples/interpreter/AST.java b/jflex/examples/cup-interpreter/src/main/java/AST.java similarity index 71% rename from jflex/examples/interpreter/AST.java rename to jflex/examples/cup-interpreter/src/main/java/AST.java index efa183df6..19638abc8 100644 --- a/jflex/examples/interpreter/AST.java +++ b/jflex/examples/cup-interpreter/src/main/java/AST.java @@ -8,12 +8,10 @@ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /** - * General interface for nodes in the abstract syntax tree. Contains - * only the method toString which is already inherited from Object, - * so the interface doesn't add any functionality. It only provides - * a common super type for all elements in the AST. + * General interface for nodes in the abstract syntax tree. Contains only the method toString which + * is already inherited from Object, so the interface doesn't add any functionality. It only + * provides a common super type for all elements in the AST. */ interface AST { - public String toString(); // already inherited from Object + public String toString(); // already inherited from Object } - diff --git a/jflex/examples/cup-interpreter/src/main/java/Main.java b/jflex/examples/cup-interpreter/src/main/java/Main.java new file mode 100644 index 000000000..f61da8caa --- /dev/null +++ b/jflex/examples/cup-interpreter/src/main/java/Main.java @@ -0,0 +1,71 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Copyright (C) 2001 Gerwin Klein * + * Copyright (C) 2001 Bernhard Rumpe * + * All rights reserved. * + * * + * License: BSD * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +import java.io.*; + +/** + * Main program of the interpreter for the AS programming language. Based on JFlex/CUP. + * + *

Steps: + * + *

    + *
  1. scanning (Yylex) + *
  2. context free parsing and AST building (yyparse) + *
  3. build up symbol table (setSymtabs) + *
  4. check context conditions (checkcontext) + *
  5. prepare interpretation (prepInterp) + *
  6. start interpretation (interpret) + *
+ */ +public class Main { + + public static void main(String[] args) throws Exception { + Reader reader = null; + + if (args.length == 1) { + File input = new File(args[0]); + if (!input.canRead()) { + System.err.println("Error: could not read [" + input + "]"); + } + reader = new FileReader(input); + } else { + reader = new InputStreamReader(System.in); + } + + Yylex scanner = new Yylex(reader); // create scanner + SymTab symtab = new SymTab(); // set global symbol table + scanner.setSymtab(symtab); + + parser parser = new parser(scanner); // create parser + Tprogram syntaxbaum = null; + + try { + syntaxbaum = (Tprogram) parser.parse().value; // parse + } catch (Exception e) { + e.printStackTrace(); + } + + System.out.println(syntaxbaum); + + syntaxbaum.setSymtabs(); // set symbol table + + syntaxbaum.checkcontext(); // CoCo (DefVar, DefFun, Arity) + if (contexterror > 0) return; + + syntaxbaum.prepInterp(); // var. indices and function pointers + // im Syntaxbaum setzen + syntaxbaum.interpret(); // interpretation + } + + static int contexterror = 0; // number of errors in context conditions + + public static void error(String s) { + System.out.println((contexterror++) + ". " + s); + } +} diff --git a/jflex/examples/interpreter/STEfun.java b/jflex/examples/cup-interpreter/src/main/java/STEfun.java similarity index 68% rename from jflex/examples/interpreter/STEfun.java rename to jflex/examples/cup-interpreter/src/main/java/STEfun.java index 46e47f53b..9bbee819b 100644 --- a/jflex/examples/interpreter/STEfun.java +++ b/jflex/examples/cup-interpreter/src/main/java/STEfun.java @@ -7,37 +7,34 @@ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - /** * Symbol table entry for functions. - * - * Contains arity and reference to location of definition - */ + * + *

Contains arity and reference to location of definition + */ class STEfun extends SymtabEntry { int arity; Tdekl dekl; // location of definition - - public STEfun(String f, Tdekl d, int a) { + + public STEfun(String f, Tdekl d, int a) { super(f); - dekl=d; - arity=a; + dekl = d; + arity = a; } - - public int kind() { - return SymtabEntry.FUN; + + public int kind() { + return SymtabEntry.FUN; } - public String toString() { - return "function "+name+", arity "+arity; + public String toString() { + return "function " + name + ", arity " + arity; } - public int arity() { - return arity; + public int arity() { + return arity; } - public Tdekl getDekl() { - return dekl; + public Tdekl getDekl() { + return dekl; } } - - diff --git a/jflex/examples/interpreter/STEvar.java b/jflex/examples/cup-interpreter/src/main/java/STEvar.java similarity index 69% rename from jflex/examples/interpreter/STEvar.java rename to jflex/examples/cup-interpreter/src/main/java/STEvar.java index e813edc11..03bb80dde 100644 --- a/jflex/examples/interpreter/STEvar.java +++ b/jflex/examples/cup-interpreter/src/main/java/STEvar.java @@ -7,40 +7,35 @@ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - /** * Symbol table entry for variables. - * - * Contains index in the parameter list and a flag if it - * is an input variable. - */ + * + *

Contains index in the parameter list and a flag if it is an input variable. + */ class STEvar extends SymtabEntry { - boolean is_input; - int index; + boolean is_input; + int index; public STEvar(String v, boolean ii, int ind) { super(v); - is_input=ii; - index=ind; + is_input = ii; + index = ind; } public int kind() { - return SymtabEntry.VAR; + return SymtabEntry.VAR; } public String toString() { - if (is_input) - return "input var "+name+" ("+index+")"; - else - return "parameter "+name+" ("+index+")"; + if (is_input) return "input var " + name + " (" + index + ")"; + else return "parameter " + name + " (" + index + ")"; } public int getIndex() { - return index; + return index; } public boolean isInput() { - return is_input; + return is_input; } } - diff --git a/jflex/examples/interpreter/SymTab.java b/jflex/examples/cup-interpreter/src/main/java/SymTab.java similarity index 60% rename from jflex/examples/interpreter/SymTab.java rename to jflex/examples/cup-interpreter/src/main/java/SymTab.java index c82d11713..7f2634e22 100644 --- a/jflex/examples/interpreter/SymTab.java +++ b/jflex/examples/cup-interpreter/src/main/java/SymTab.java @@ -7,56 +7,53 @@ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - import java.util.*; /** - * Symbol table for the interpreter, contains information about - * variables and functions. - * - * For every binding location of a name a symbol will be created. - * The symbol tables are connected hierarchically by pointers to - * the predecessor. Lookup takes predecessors into account. - */ + * Symbol table for the interpreter, contains information about variables and functions. + * + *

For every binding location of a name a symbol will be created. The symbol tables are connected + * hierarchically by pointers to the predecessor. Lookup takes predecessors into account. + */ public class SymTab { - Map m; // contains the list of words - SymTab pred; // predecessor symbol table (if exists) + /** contains the list of words. */ + Map m; + /** predecessor symbol table (if exists) */ + SymTab pred; public SymTab() { - this(null); + this(null); } public SymTab(SymTab p) { - m = new HashMap(); + m = new HashMap(); pred = p; } public boolean enter(String s, SymtabEntry e) { Object value = lookup(s); m.put(s, e); - return(value==null); + return (value == null); } public SymtabEntry lookup(String s) { SymtabEntry value = m.get(s); - if (value==null && pred!=null) - value = pred.lookup(s); + if (value == null && pred != null) value = pred.lookup(s); return value; } - public String toString() { // for output with print + public String toString() { // for output with print StringBuilder res = new StringBuilder("symbol table\n=============\n"); - - for (Map.Entry entry : m.entrySet()) + + for (Map.Entry entry : m.entrySet()) res.append(entry.getKey()).append(" \t").append(entry.getValue()).append("\n"); - if (pred != null) - res.append("++ predecessor!\n"); + if (pred != null) res.append("++ predecessor!\n"); return res.toString(); } public int size() { - return(m.size()); + return (m.size()); } } diff --git a/jflex/examples/interpreter/SymtabEntry.java b/jflex/examples/cup-interpreter/src/main/java/SymtabEntry.java similarity index 77% rename from jflex/examples/interpreter/SymtabEntry.java rename to jflex/examples/cup-interpreter/src/main/java/SymtabEntry.java index f196b4fae..d1aa0c8fd 100644 --- a/jflex/examples/interpreter/SymtabEntry.java +++ b/jflex/examples/cup-interpreter/src/main/java/SymtabEntry.java @@ -7,31 +7,27 @@ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - /** - * Symbol table entry for names, there are subclasses for - * variables and functions. - * - * Defines constants UNKNOWN, VAR and FUN as kinds of - * symbol table entries. - */ + * Symbol table entry for names, there are subclasses for variables and functions. + * + *

Defines constants UNKNOWN, VAR and FUN as kinds of symbol table entries. + */ class SymtabEntry { String name; public SymtabEntry(String v) { - name=v; + name = v; } public int kind() { - return UNKNOWN; + return UNKNOWN; } public String toString() { - return("unknown "+name); + return ("unknown " + name); } static final int UNKNOWN = 12; static final int VAR = 13; static final int FUN = 14; } - diff --git a/jflex/examples/interpreter/Tboolexp.java b/jflex/examples/cup-interpreter/src/main/java/Tboolexp.java similarity index 56% rename from jflex/examples/interpreter/Tboolexp.java rename to jflex/examples/cup-interpreter/src/main/java/Tboolexp.java index f532a0a03..b87568f2f 100644 --- a/jflex/examples/interpreter/Tboolexp.java +++ b/jflex/examples/cup-interpreter/src/main/java/Tboolexp.java @@ -7,47 +7,48 @@ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -/** - * AST node for bool expressions - */ +/** AST node for bool expressions */ class Tboolexp implements AST { - Texp exp1, exp2; // left and right subexpression - char kind; // '=', '<' and '!' for "<=" + /** Left subexpression. */ + Texp exp1; + /** Right subexpression. */ + Texp exp2; + /** {@code '='}, {@code '<'} and {@code '!'} for "<=". */ + char kind; public Tboolexp(Texp e1, char k, Texp e2) { - exp1=e1; - kind=k; - exp2=e2; + exp1 = e1; + kind = k; + exp2 = e2; } public String toString() { - if (kind!='!') - return(""+exp1+kind+exp2); - else - return(exp1+"<="+exp2); + if (kind != '!') return ("" + exp1 + kind + exp2); + else return (exp1 + "<=" + exp2); } - public void checkcontext(SymTab st) { // context conditions + public void checkcontext(SymTab st) { // context conditions exp1.checkcontext(st); exp2.checkcontext(st); } - public void prepInterp(SymTab st) { // set pointers and indices + public void prepInterp(SymTab st) { // set pointers and indices exp1.prepInterp(st); exp2.prepInterp(st); } public boolean interpret(int[] in, int[] par) { - int e1 = exp1.interpret(in,par); - int e2 = exp2.interpret(in,par); - switch(kind) { - case '=': return(e1==e2); - case '<': return(e1 * + * Copyright (C) 2001 Bernhard Rumpe * + * All rights reserved. * + * * + * License: BSD * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/** + * AST node for function declarations. + * + *

Also contains a reference to the symbol table of the parameters and their arity. + */ +class Tdekl implements AST { + /** identifier */ + Tident ident; // + /** list of parameters */ + Tparlist parlist; + /** function body */ + Texp exp; + + public Tdekl(Tident i, Tparlist p, Texp e) { + parlist = p; + ident = i; + exp = e; + } + + public String toString() { + return (ident + "(" + parlist + ") = \n " + exp); + } + + SymTab params; // symbol table of the parameters + int arity; + + public void setSymtab(SymTab st) { + params = new SymTab(st); + parlist.setSymtab(params, false, 0); + arity = params.size(); + + boolean isNew = st.enter(ident.toString(), new STEfun(ident.toString(), this, arity)); + // CoCo (Fun) + if (!isNew) Main.error("funktion " + ident + " defined twice!"); + } + + public void printSymtabs() { + System.out.print("funktion " + ident.toString() + "\n" + params); + } + + public void checkcontext() { + exp.checkcontext(params); // CoCo (DefFun,DefVar,Arity) + } + + public void prepInterp(SymTab st) { // set pointers and indices + exp.prepInterp(params); + } + + public int interpret(int[] in, int[] par) { + return (exp.interpret(in, par)); + } + + public int arity() { + return (arity); + } +} diff --git a/jflex/examples/interpreter/Tdekllist.java b/jflex/examples/cup-interpreter/src/main/java/Tdekllist.java similarity index 56% rename from jflex/examples/interpreter/Tdekllist.java rename to jflex/examples/cup-interpreter/src/main/java/Tdekllist.java index a6e126904..6ffca6657 100644 --- a/jflex/examples/interpreter/Tdekllist.java +++ b/jflex/examples/cup-interpreter/src/main/java/Tdekllist.java @@ -7,52 +7,45 @@ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -/** - * AST node for declaration lists of functions - */ +/** AST node for declaration lists of functions */ class Tdekllist implements AST { - Tdekllist dekllist; // rest list (optional null) - Tdekl dekl; // declaration + /** rest list (optional null) */ + Tdekllist dekllist; + /** declaration */ + Tdekl dekl; public Tdekllist(Tdekllist p, Tdekl e) { - dekllist=p; - dekl=e; + dekllist = p; + dekl = e; } public Tdekllist(Tdekl e) { - dekllist=null; - dekl=e; + dekllist = null; + dekl = e; } public String toString() { - if (dekllist!=null) - return(dekllist+",\n"+dekl); - else - return(dekl.toString()); + if (dekllist != null) return (dekllist + ",\n" + dekl); + else return (dekl.toString()); } public void setSymtab(SymTab st) { - if (dekllist!=null) - dekllist.setSymtab(st); + if (dekllist != null) dekllist.setSymtab(st); dekl.setSymtab(st); } public void printSymtabs() { - if (dekllist!=null) - dekllist.printSymtabs(); + if (dekllist != null) dekllist.printSymtabs(); dekl.printSymtabs(); } - + public void checkcontext() { - if (dekllist!=null) - dekllist.checkcontext(); - dekl.checkcontext(); // CoCo (DefFun,DefVar,Arity) - } // in function body + if (dekllist != null) dekllist.checkcontext(); + dekl.checkcontext(); // CoCo (DefFun,DefVar,Arity) + } // in function body - public void prepInterp(SymTab st) { // set pointers and indices + public void prepInterp(SymTab st) { // set pointers and indices dekl.prepInterp(st); - if (dekllist!=null) dekllist.prepInterp(st); + if (dekllist != null) dekllist.prepInterp(st); } } - diff --git a/jflex/examples/interpreter/Texp.java b/jflex/examples/cup-interpreter/src/main/java/Texp.java similarity index 61% rename from jflex/examples/interpreter/Texp.java rename to jflex/examples/cup-interpreter/src/main/java/Texp.java index a5ebae093..59bbc9602 100644 --- a/jflex/examples/interpreter/Texp.java +++ b/jflex/examples/cup-interpreter/src/main/java/Texp.java @@ -7,26 +7,23 @@ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - /** * AST node for an integer expression. - * - * The non terminal exp is the sum of multiple variants and - * therefore modelled as an abstract class. - * - * The interpretation function interpret is called with - * valuations of input variables in and parameters - * par. Before interpret can be called, pointers - * and variable indices must be set with prepInterp. - */ + * + *

The non terminal exp is the sum of multiple variants and therefore modelled as an abstract + * class. + * + *

The interpretation function interpret is called with valuations of input variables + * in and parameters par. Before interpret can be called, pointers and variable + * indices must be set with prepInterp. + */ abstract class Texp implements AST { // test context conditions (DefFun,DefVar,Arity) - abstract public void checkcontext(SymTab st); - + public abstract void checkcontext(SymTab st); + // set pointers and indices for variables and functions - abstract public void prepInterp(SymTab st); - + public abstract void prepInterp(SymTab st); + // interpretation - abstract public int interpret(int[] in, int[] par); + public abstract int interpret(int[] in, int[] par); } - diff --git a/jflex/examples/interpreter/Texpinfix.java b/jflex/examples/cup-interpreter/src/main/java/Texpinfix.java similarity index 61% rename from jflex/examples/interpreter/Texpinfix.java rename to jflex/examples/cup-interpreter/src/main/java/Texpinfix.java index a12222ed7..d3aae47ca 100644 --- a/jflex/examples/interpreter/Texpinfix.java +++ b/jflex/examples/cup-interpreter/src/main/java/Texpinfix.java @@ -7,22 +7,20 @@ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -/** - * AST node for infix expressions - */ +/** AST node for infix expressions */ class Texpinfix extends Texp implements AST { - Texp exp1, exp2; // left and right sub expression - char kind; // kind ('+', '-', '*', '/') + Texp exp1, exp2; // left and right sub expression + /** kind ('+', '-', '*', '/') */ + char kind; public Texpinfix(Texp e1, char k, Texp e2) { - exp1=e1; - kind=k; - exp2=e2; + exp1 = e1; + kind = k; + exp2 = e2; } public String toString() { - return("("+exp1+kind+exp2+")"); + return ("(" + exp1 + kind + exp2 + ")"); } public void checkcontext(SymTab st) { // context conditions @@ -30,24 +28,26 @@ public void checkcontext(SymTab st) { // context conditions exp2.checkcontext(st); } - public void prepInterp(SymTab st) { // set pointers und indices + public void prepInterp(SymTab st) { // set pointers und indices exp1.prepInterp(st); exp2.prepInterp(st); } - public int interpret(int[] in, int[] par) { - int e1 = exp1.interpret(in,par); - int e2 = exp2.interpret(in,par); + public int interpret(int[] in, int[] par) { + int e1 = exp1.interpret(in, par); + int e2 = exp2.interpret(in, par); switch (kind) { - case '+': return(e1+e2); - case '-': return(e1-e2); - case '*': return(e1*e2); - case '/': return(e1/e2); + case '+': + return (e1 + e2); + case '-': + return (e1 - e2); + case '*': + return (e1 * e2); + case '/': + return (e1 / e2); } - return -1; // error + return -1; // error } } - - diff --git a/jflex/examples/interpreter/Texplist.java b/jflex/examples/cup-interpreter/src/main/java/Texplist.java similarity index 51% rename from jflex/examples/interpreter/Texplist.java rename to jflex/examples/cup-interpreter/src/main/java/Texplist.java index fc48a29b7..97e1214da 100644 --- a/jflex/examples/interpreter/Texplist.java +++ b/jflex/examples/cup-interpreter/src/main/java/Texplist.java @@ -7,56 +7,48 @@ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - /** * AST node for a list of expressions. - * - * The interpretation of a list of expressions stores the - * results of the expressions in an array that can be used - * as parameter list for function calls. - */ + * + *

The interpretation of a list of expressions stores the results of the expressions in an array + * that can be used as parameter list for function calls. + */ class Texplist implements AST { - Texplist explist; // next list element (optional null) - Texp exp; // expression of this list node + Texplist explist; // next list element (optional null) + Texp exp; // expression of this list node public Texplist(Texplist p, Texp e) { - explist=p; - exp=e; + explist = p; + exp = e; } public Texplist(Texp e) { - explist=null; - exp=e; + explist = null; + exp = e; } public String toString() { - if (explist!=null) - return explist+","+exp; - else - return exp.toString(); + if (explist != null) return explist + "," + exp; + else return exp.toString(); } public void checkcontext(SymTab st) { - if (explist!=null) - explist.checkcontext(st); - exp.checkcontext(st); // CoCo (DefFun,DefVar,Arity) - } // in expression + if (explist != null) explist.checkcontext(st); + exp.checkcontext(st); // CoCo (DefFun,DefVar,Arity) + } // in expression public int length() { - if (explist!=null) - return 1+explist.length(); - else - return 1; + if (explist != null) return 1 + explist.length(); + else return 1; } - - public void prepInterp(SymTab st) { // set pointers and indices + + public void prepInterp(SymTab st) { // set pointers and indices exp.prepInterp(st); - if (explist!=null) explist.prepInterp(st); + if (explist != null) explist.prepInterp(st); } - + public void interpret(int[] in, int[] par, int[] res, int index) { - res[index] = exp.interpret(in,par); - if (explist!=null) explist.interpret(in,par,res,index+1); + res[index] = exp.interpret(in, par); + if (explist != null) explist.interpret(in, par, res, index + 1); } } - diff --git a/jflex/examples/interpreter/Tfun.java b/jflex/examples/cup-interpreter/src/main/java/Tfun.java similarity index 57% rename from jflex/examples/interpreter/Tfun.java rename to jflex/examples/cup-interpreter/src/main/java/Tfun.java index 8b329db13..2f5d9a5a3 100644 --- a/jflex/examples/interpreter/Tfun.java +++ b/jflex/examples/cup-interpreter/src/main/java/Tfun.java @@ -7,49 +7,44 @@ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - /** * AST node for function application. - * - * Also contains pointer to declaration location of the function. - */ + * + *

Also contains pointer to declaration location of the function. + */ class Tfun extends Texp implements AST { - Tident ident; // name of the function - Texplist explist; // parameter list + Tident ident; // name of the function + Texplist explist; // parameter list public Tfun(Tident i, Texplist e) { - ident=i; - explist=e; + ident = i; + explist = e; } public String toString() { - return ident+"("+explist+")"; + return ident + "(" + explist + ")"; } public void checkcontext(SymTab st) { // CoCo (DefFun,Arity) explist.checkcontext(st); SymtabEntry ste = st.lookup(ident.toString()); - if (ste==null) - Main.error("function not defined: "+ident); - else if (ste.kind() != SymtabEntry.FUN) - Main.error("variable used as funktion: "+ident); - else if (((STEfun)ste).arity() != explist.length()) - Main.error("wrong arity at function call: "+ident); + if (ste == null) Main.error("function not defined: " + ident); + else if (ste.kind() != SymtabEntry.FUN) Main.error("variable used as funktion: " + ident); + else if (((STEfun) ste).arity() != explist.length()) + Main.error("wrong arity at function call: " + ident); } - Tdekl fundekl; // pointer to location of function declaration + Tdekl fundekl; // pointer to location of function declaration - // set pointers and indices + // set pointers and indices public void prepInterp(SymTab st) { - fundekl = ((STEfun)st.lookup(ident.toString())).getDekl(); + fundekl = ((STEfun) st.lookup(ident.toString())).getDekl(); explist.prepInterp(st); } public int interpret(int[] in, int[] par) { int[] newparams = new int[fundekl.arity()]; - explist.interpret(in,par,newparams,0); - return fundekl.interpret(in,newparams); + explist.interpret(in, par, newparams, 0); + return fundekl.interpret(in, newparams); } } - - diff --git a/jflex/examples/interpreter/Tident.java b/jflex/examples/cup-interpreter/src/main/java/Tident.java similarity index 57% rename from jflex/examples/interpreter/Tident.java rename to jflex/examples/cup-interpreter/src/main/java/Tident.java index a33fc4485..215d2f4e0 100644 --- a/jflex/examples/interpreter/Tident.java +++ b/jflex/examples/cup-interpreter/src/main/java/Tident.java @@ -7,45 +7,36 @@ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -/** - * AST node for an identifier - */ +/** AST node for an identifier */ class Tident extends Texp implements AST { - String name; + String name; public Tident(String s) { - name = s; + name = s; } public String toString() { - return name; + return name; } - public void checkcontext(SymTab st) { // CoCo (DefVar) + public void checkcontext(SymTab st) { // CoCo (DefVar) SymtabEntry ste = st.lookup(name); - if (ste==null) - Main.error("variable not defined: "+name); - else if (ste.kind() != SymtabEntry.VAR) - Main.error("function used as variable: "+name); + if (ste == null) Main.error("variable not defined: " + name); + else if (ste.kind() != SymtabEntry.VAR) Main.error("function used as variable: " + name); } - int index; // number of ident in environment - boolean is_input; // is it an input variable? + int index; // number of ident in environment + boolean is_input; // is it an input variable? - public void prepInterp(SymTab st) { // set index for environment - STEvar ste = (STEvar)st.lookup(name); + public void prepInterp(SymTab st) { // set index for environment + STEvar ste = (STEvar) st.lookup(name); index = ste.getIndex(); is_input = ste.isInput(); } public int interpret(int[] in, int[] par) { - if (is_input) - return(in[index]); - else - return(par[index]); + if (is_input) return (in[index]); + else return (par[index]); } } - - diff --git a/jflex/examples/interpreter/Tifthenelse.java b/jflex/examples/cup-interpreter/src/main/java/Tifthenelse.java similarity index 72% rename from jflex/examples/interpreter/Tifthenelse.java rename to jflex/examples/cup-interpreter/src/main/java/Tifthenelse.java index 03aefa3fc..6028be6f2 100644 --- a/jflex/examples/interpreter/Tifthenelse.java +++ b/jflex/examples/cup-interpreter/src/main/java/Tifthenelse.java @@ -7,22 +7,23 @@ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -/** - * AST node for if-then-else expressions - */ +/** AST node for if-then-else expressions */ class Tifthenelse extends Texp implements AST { - Tboolexp boolexp; // condition - Texp exp1, exp2; // then and else branch - + /** condition* */ + Tboolexp boolexp; + /** then branch */ + Texp exp1; + /** else branch */ + Texp exp2; + public Tifthenelse(Tboolexp b, Texp e1, Texp e2) { - boolexp=b; - exp1=e1; - exp2=e2; + boolexp = b; + exp1 = e1; + exp2 = e2; } - + public String toString() { - return "if "+boolexp+" then "+exp1+" else "+exp2+" fi"; + return "if " + boolexp + " then " + exp1 + " else " + exp2 + " fi"; } public void checkcontext(SymTab st) { @@ -38,11 +39,8 @@ public void prepInterp(SymTab st) { } public int interpret(int[] in, int[] par) { - boolean b = boolexp.interpret(in,par); - if (b) - return exp1.interpret(in,par); - else - return exp2.interpret(in,par); + boolean b = boolexp.interpret(in, par); + if (b) return exp1.interpret(in, par); + else return exp2.interpret(in, par); } } - diff --git a/jflex/examples/interpreter/Tnumber.java b/jflex/examples/cup-interpreter/src/main/java/Tnumber.java similarity index 73% rename from jflex/examples/interpreter/Tnumber.java rename to jflex/examples/cup-interpreter/src/main/java/Tnumber.java index 149907130..83b467b70 100644 --- a/jflex/examples/interpreter/Tnumber.java +++ b/jflex/examples/cup-interpreter/src/main/java/Tnumber.java @@ -7,30 +7,27 @@ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -/** - * AST node for a number - */ +/** AST node for a number */ class Tnumber extends Texp implements AST { - int n; // value of the number + int n; // value of the number public Tnumber(String s) { - try { n = Integer.parseInt(s); } - catch (NumberFormatException e) { n=-1; }; + try { + n = Integer.parseInt(s); + } catch (NumberFormatException e) { + n = -1; + } } public String toString() { - return(""+n); + return String.valueOf(n); } - public void checkcontext(SymTab st) { - } + public void checkcontext(SymTab st) {} - public void prepInterp(SymTab st) { - } + public void prepInterp(SymTab st) {} public int interpret(int[] in, int[] par) { - return(n); + return n; } } - diff --git a/jflex/examples/interpreter/Tparlist.java b/jflex/examples/cup-interpreter/src/main/java/Tparlist.java similarity index 59% rename from jflex/examples/interpreter/Tparlist.java rename to jflex/examples/cup-interpreter/src/main/java/Tparlist.java index 5ab3aa23c..5658deb39 100644 --- a/jflex/examples/interpreter/Tparlist.java +++ b/jflex/examples/cup-interpreter/src/main/java/Tparlist.java @@ -7,37 +7,32 @@ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -/** - * AST node for a parameter list. - */ +/** AST node for a parameter list. */ class Tparlist implements AST { - Tparlist parlist; // rest of the liste (optional null) - Tident ident; // identifier + /** Rest of the list (optional null) */ + Tparlist parlist; + /** identifier */ + Tident ident; public Tparlist(Tparlist p, Tident i) { - parlist=p; - ident=i; + parlist = p; + ident = i; } public Tparlist(Tident i) { - parlist=null; - ident=i; + parlist = null; + ident = i; } public String toString() { - if (parlist!=null) - return parlist+","+ident; - else - return ident.toString(); + if (parlist != null) return parlist + "," + ident; + else return ident.toString(); } public void setSymtab(SymTab st, boolean isInput, int index) { - boolean isNew = st.enter(ident.toString(), - new STEvar(ident.toString(), isInput, index)); - - if (!isNew) Main.error("Variable "+ident+" defined twice!"); - if (parlist!=null) parlist.setSymtab(st, isInput, index+1); + boolean isNew = st.enter(ident.toString(), new STEvar(ident.toString(), isInput, index)); + + if (!isNew) Main.error("Variable " + ident + " defined twice!"); + if (parlist != null) parlist.setSymtab(st, isInput, index + 1); } } - diff --git a/jflex/examples/cup-interpreter/src/main/java/Tprogram.java b/jflex/examples/cup-interpreter/src/main/java/Tprogram.java new file mode 100644 index 000000000..2c1e9f97a --- /dev/null +++ b/jflex/examples/cup-interpreter/src/main/java/Tprogram.java @@ -0,0 +1,89 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Copyright (C) 2001 Gerwin Klein * + * Copyright (C) 2001 Bernhard Rumpe * + * All rights reserved. * + * * + * License: BSD * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/** + * AST node for the whole program (top node). + * + *

Also contains two symbol tables, one for input variables, one for function names. + * + *

All operations like context check, symbol table build up etc. start here. + */ +class Tprogram implements AST { + + Tparlist parlist; // input variables + Tdekllist dekllist; // function declarations + Texplist explist; // result expressions + Texplist arguments; // input values + + public Tprogram(Tparlist p, Tdekllist d, Texplist e, Texplist a) { + parlist = p; + dekllist = d; + explist = e; + arguments = a; + } + + public String toString() { + return ("Program:\n=============\ninput " + + parlist + + "\nfunctions\n" + + dekllist + + "\noutput " + + explist + + "\narguments " + + arguments + + "\nend"); + } + + SymTab inputs; // table of input variables + SymTab functions; // table of functions + + public void setSymtabs() { // calculate symbol table entries + inputs = new SymTab(); // set input variables + parlist.setSymtab(inputs, true, 0); + functions = new SymTab(inputs); + dekllist.setSymtab(functions); + } + + public void printSymtabs() { + System.out.print("Input variables-\n" + inputs); + System.out.print("Functions-\n" + functions); + dekllist.printSymtabs(); + } + + public void checkcontext() { + dekllist.checkcontext(); // CoCo (DefFun,DefVar,Arity) + // in function bodies + explist.checkcontext(functions); // CoCo (DefFun,DefVar,Arity) + // in result expressions + arguments.checkcontext(new SymTab()); // CoCo (constants) + // in arguments + if (arguments.length() != inputs.size()) + Main.error("Argument list and input variables list differ!"); + } + + public void prepInterp() { // set pointers and indices + dekllist.prepInterp(functions); + explist.prepInterp(functions); + } + + public void interpret() { + int[] inputEnv = new int[inputs.size()]; // set input + + arguments.interpret(null, null, inputEnv, 0); + + System.out.println("Result:\n============="); + + int[] ergebnis = new int[explist.length()]; + explist.interpret(inputEnv, null, ergebnis, 0); // calculate result + + int i; + for (i = explist.length() - 1; i > 0; i--) System.out.print(ergebnis[i] + ", "); + System.out.println(ergebnis[i]); + } +} diff --git a/jflex/examples/interpreter/Tuminus.java b/jflex/examples/cup-interpreter/src/main/java/Tuminus.java similarity index 74% rename from jflex/examples/interpreter/Tuminus.java rename to jflex/examples/cup-interpreter/src/main/java/Tuminus.java index 3d2212cbc..9bc44dc8f 100644 --- a/jflex/examples/interpreter/Tuminus.java +++ b/jflex/examples/cup-interpreter/src/main/java/Tuminus.java @@ -7,32 +7,28 @@ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -/** - * AST node for unary minus expressions. - */ +/** AST node for unary minus expressions. */ class Tuminus extends Texp implements AST { - - Texp exp; // the negated expression + /** the negated expression */ + Texp exp; public Tuminus(Texp e) { - exp=e; + exp = e; } public String toString() { - return "-"+exp; + return "-" + exp; } public void checkcontext(SymTab st) { - exp.checkcontext(st); + exp.checkcontext(st); } - public void prepInterp(SymTab st) { - exp.prepInterp(st); + public void prepInterp(SymTab st) { + exp.prepInterp(st); } public int interpret(int[] in, int[] par) { - return -(exp.interpret(in,par)); + return -(exp.interpret(in, par)); } } - diff --git a/jflex/examples/interpreter/scanner.flex b/jflex/examples/cup-interpreter/src/main/jflex/scanner.flex similarity index 100% rename from jflex/examples/interpreter/scanner.flex rename to jflex/examples/cup-interpreter/src/main/jflex/scanner.flex diff --git a/jflex/examples/interpreter/example.as b/jflex/examples/cup-interpreter/src/test/data/example.as similarity index 100% rename from jflex/examples/interpreter/example.as rename to jflex/examples/cup-interpreter/src/test/data/example.as diff --git a/jflex/examples/interpreter/output.good b/jflex/examples/cup-interpreter/src/test/data/output.good similarity index 100% rename from jflex/examples/interpreter/output.good rename to jflex/examples/cup-interpreter/src/test/data/output.good diff --git a/jflex/examples/cup-java/Makefile b/jflex/examples/cup-java/Makefile new file mode 100644 index 000000000..124f532a5 --- /dev/null +++ b/jflex/examples/cup-java/Makefile @@ -0,0 +1,38 @@ +# You need CUP v0.10j (or newer) for this makefile to work (for java12.cup) +# +# CUP classes should be included in CLASSPATH + +ROOT = ../../.. +JFLEX = $(ROOT)/jflex/bin/jflex +CUPJAR = $(ROOT)/cup/cup/java-cup-11b.jar +CUP = java -jar $(CUPJAR) + + +all: clean compile + +compile: gen_all src/main/java/*.java + javac -d out -cp $(CUPJAR) out/genfiles/*.java src/main/java/*.java + +test: out/lexer-output.txt + @(diff $< src/test/data/lexer-output.good && echo "Test OK") || (echo "Test FAILED"; exit 1) + +out/lexer-output.txt: compile outdir + java -cp out:$(CUPJAR) TestLexer src/main/java/TestLexer.java > out/lexer-output.txt + +gen_all: gen_parser gen_scanner gen_unicode + +gen_parser: src/main/cup/java12.cup outdir + $(CUP) -interface $< + mv parser.java sym.java out/genfiles + +gen_scanner: src/main/jflex/java.flex outdir + $(JFLEX) -d out/genfiles $< + +gen_unicode: src/main/jflex/unicode.flex outdir + $(JFLEX) -d out/genfiles $< + +clean: + rm -rf out + +outdir: + mkdir -p out/genfiles diff --git a/jflex/examples/java/README.md b/jflex/examples/cup-java/README.md similarity index 63% rename from jflex/examples/java/README.md rename to jflex/examples/cup-java/README.md index aca25bbf3..c21e9bd40 100644 --- a/jflex/examples/java/README.md +++ b/jflex/examples/cup-java/README.md @@ -3,33 +3,42 @@ JFlex: Complex example (with CUP) This directory contains a scanner and parser for the Java programming language (Java 1.2). -## Prerequisite +## Build, run, test -You need the parser generator CUP v0.11a for the parser to work. +### Using Maven -## Build and run + ../../mvnw package + +To run the parser: + + java -cp target/cup-java-1.0.jar:../../../cup/cup/java-cup-11b.jar JavaParser + +or more simply the uberjar version: + + java -jar target/cup-java-full-1.0.jar + + +### Using ant + + ant compile + ant run + java -jar build:../../../cup/cup/java-cup-11b.jar JavaParser + +### Using make Use the Makefile or Ant (via 'ant run') to generate the lexer and parser, or type: -``` -jflex unicode.flex -jflex java.flex -java java_cup.Main -interface < java12.cup -javac JavaParser.java TestLexer.java -``` + make compile + make test The parser can be tested with: -``` -java JavaParser -``` + java -cp out:../../../cup/cup/java-cup-11b.jar JavaParser The scanner (without parser attached) can be test with: -``` -java TestLexer -``` + java -cp out:../../../cup/cup/java-cup-11b.jar TestLexer ## Files diff --git a/jflex/examples/cup-java/build.xml b/jflex/examples/cup-java/build.xml new file mode 100644 index 000000000..5d566d926 --- /dev/null +++ b/jflex/examples/cup-java/build.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/jflex/examples/cup-java/pom.xml b/jflex/examples/cup-java/pom.xml new file mode 100644 index 000000000..3e37e6fa9 --- /dev/null +++ b/jflex/examples/cup-java/pom.xml @@ -0,0 +1,99 @@ + + + 4.0.0 + de.flex.examples + cup-java + 1.0 + A parser for Java + An parser for the Java programming language. + This simple grammar can only parse Java up to version 1.2. + It uses JFlex and CUP. + + + de.jflex + cup_runtime + 11b + + + junit + junit + 3.8.2 + test + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.0 + + 1.6 + 1.6 + + + + de.jflex + jflex-maven-plugin + 1.7.1-SNAPSHOT + + + + generate + + + + + + + + de.jflex + cup-maven-plugin + 1.0 + + + + generate + + + true + + + + + + + + + + org.apache.maven.plugins + maven-shade-plugin + + + package + + shade + + + ${project.artifactId}-full-${project.version} + + + JavaParser + + + + + de.jflex:cup_runtime + + + + + + + + + + UTF-8 + + diff --git a/jflex/examples/java/java12.cup b/jflex/examples/cup-java/src/main/cup/java12.cup similarity index 100% rename from jflex/examples/java/java12.cup rename to jflex/examples/cup-java/src/main/cup/java12.cup diff --git a/jflex/examples/java/JavaParser.java b/jflex/examples/cup-java/src/main/java/JavaParser.java similarity index 79% rename from jflex/examples/java/JavaParser.java rename to jflex/examples/cup-java/src/main/java/JavaParser.java index 131b10463..4e26597e0 100644 --- a/jflex/examples/java/JavaParser.java +++ b/jflex/examples/cup-java/src/main/java/JavaParser.java @@ -8,10 +8,8 @@ import java.io.*; - /** - * Simple test driver for the java parser. Just runs it on some - * input files, gives no useful output. + * Simple test driver for the java parser. Just runs it on some input files, gives no useful output. */ public class JavaParser { @@ -19,18 +17,16 @@ public static void main(String argv[]) { for (int i = 0; i < argv.length; i++) { try { - System.out.println("Parsing ["+argv[i]+"]"); + System.out.println("Parsing [" + argv[i] + "]"); Scanner s = new Scanner(new UnicodeEscapes(new FileReader(argv[i]))); parser p = new parser(s); p.parse(); - + System.out.println("No errors."); - } - catch (Exception e) { - e.printStackTrace(System.out); + } catch (Exception e) { + e.printStackTrace(System.err); System.exit(1); } } } - } diff --git a/jflex/examples/java/JavaSymbol.java b/jflex/examples/cup-java/src/main/java/JavaSymbol.java similarity index 75% rename from jflex/examples/java/JavaSymbol.java rename to jflex/examples/cup-java/src/main/java/JavaSymbol.java index d27b63dfe..af100fc33 100644 --- a/jflex/examples/java/JavaSymbol.java +++ b/jflex/examples/cup-java/src/main/java/JavaSymbol.java @@ -1,4 +1,3 @@ - public class JavaSymbol extends java_cup.runtime.Symbol { private int line; private int column; @@ -25,7 +24,13 @@ public int getColumn() { return column; } - public String toString() { - return "line "+line+", column "+column+", sym: "+sym+(value == null ? "" : (", value: '"+value+"'")); + public String toString() { + return "line " + + line + + ", column " + + column + + ", sym: " + + sym + + (value == null ? "" : (", value: '" + value + "'")); } } diff --git a/jflex/examples/java/TestLexer.java b/jflex/examples/cup-java/src/main/java/TestLexer.java similarity index 78% rename from jflex/examples/java/TestLexer.java rename to jflex/examples/cup-java/src/main/java/TestLexer.java index ce98fc5b9..ab3cd4c18 100644 --- a/jflex/examples/java/TestLexer.java +++ b/jflex/examples/cup-java/src/main/java/TestLexer.java @@ -9,39 +9,37 @@ import java.io.*; import java_cup.runtime.Symbol; - /** - * Simple test driver for the java lexer. Just runs it on some - * input files and produces debug output. Needs symbol class from - * parser. + * Simple test driver for the java lexer. Just runs it on some input files and produces debug + * output. Needs symbol class from parser. */ public class TestLexer { /** some numerals to for lexer testing */ int intDec = 37; + long longDec = 37l; int intHex = 0x0001; long longHex = 0xFFFFl; int intOct = 0377; long longOc = 007l; - int smallest = -2147483648; + int smallest = -2147483648; public static void main(String argv[]) { for (int i = 0; i < argv.length; i++) { try { - System.out.println("Lexing ["+argv[i]+"]"); + System.out.println("Lexing [" + argv[i] + "]"); Scanner scanner = new Scanner(new UnicodeEscapes(new FileReader(argv[i]))); - + Symbol s; do { s = scanner.debug_next_token(); - System.out.println("token: "+s); + System.out.println("token: " + s); } while (s.sym != sym.EOF); - + System.out.println("No errors."); - } - catch (Exception e) { + } catch (Exception e) { e.printStackTrace(System.out); System.exit(1); } diff --git a/jflex/examples/java/java.flex b/jflex/examples/cup-java/src/main/jflex/java.flex similarity index 100% rename from jflex/examples/java/java.flex rename to jflex/examples/cup-java/src/main/jflex/java.flex diff --git a/jflex/examples/java/unicode.flex b/jflex/examples/cup-java/src/main/jflex/unicode.flex similarity index 100% rename from jflex/examples/java/unicode.flex rename to jflex/examples/cup-java/src/main/jflex/unicode.flex diff --git a/jflex/examples/cup-java/src/test/data/lexer-output.good b/jflex/examples/cup-java/src/test/data/lexer-output.good new file mode 100644 index 000000000..be0bf4d78 --- /dev/null +++ b/jflex/examples/cup-java/src/test/data/lexer-output.good @@ -0,0 +1,386 @@ +Lexing [src/main/java/TestLexer.java] +line:9 col:1 --import--IMPORT-- +token: line 9, column 1, sym: 23 +line:9 col:8 --java--IDENTIFIER-- +token: line 9, column 8, sym: 98, value: 'java' +line:9 col:12 --.--DOT-- +token: line 9, column 12, sym: 12 +line:9 col:13 --io--IDENTIFIER-- +token: line 9, column 13, sym: 98, value: 'io' +line:9 col:15 --.--DOT-- +token: line 9, column 15, sym: 12 +line:9 col:16 --*--MULT-- +token: line 9, column 16, sym: 14 +line:9 col:17 --;--SEMICOLON-- +token: line 9, column 17, sym: 13 +line:10 col:1 --import--IMPORT-- +token: line 10, column 1, sym: 23 +line:10 col:8 --java_cup--IDENTIFIER-- +token: line 10, column 8, sym: 98, value: 'java_cup' +line:10 col:16 --.--DOT-- +token: line 10, column 16, sym: 12 +line:10 col:17 --runtime--IDENTIFIER-- +token: line 10, column 17, sym: 98, value: 'runtime' +line:10 col:24 --.--DOT-- +token: line 10, column 24, sym: 12 +line:10 col:25 --Symbol--IDENTIFIER-- +token: line 10, column 25, sym: 98, value: 'Symbol' +line:10 col:31 --;--SEMICOLON-- +token: line 10, column 31, sym: 13 +line:16 col:1 --public--PUBLIC-- +token: line 16, column 1, sym: 24 +line:16 col:8 --class--CLASS-- +token: line 16, column 8, sym: 34 +line:16 col:14 --TestLexer--IDENTIFIER-- +token: line 16, column 14, sym: 98, value: 'TestLexer' +line:16 col:24 --{--LBRACE-- +token: line 16, column 24, sym: 16 +line:19 col:3 --int--INT-- +token: line 19, column 3, sym: 5 +line:19 col:7 --intDec--IDENTIFIER-- +token: line 19, column 7, sym: 98, value: 'intDec' +line:19 col:14 --=--EQ-- +token: line 19, column 14, sym: 18 +line:19 col:16 --37--INTEGER_LITERAL-- +token: line 19, column 16, sym: 93, value: '37' +line:19 col:18 --;--SEMICOLON-- +token: line 19, column 18, sym: 13 +line:21 col:3 --long--LONG-- +token: line 21, column 3, sym: 6 +line:21 col:8 --longDec--IDENTIFIER-- +token: line 21, column 8, sym: 98, value: 'longDec' +line:21 col:16 --=--EQ-- +token: line 21, column 16, sym: 18 +line:21 col:18 --37l--INTEGER_LITERAL-- +token: line 21, column 18, sym: 93, value: '37' +line:21 col:21 --;--SEMICOLON-- +token: line 21, column 21, sym: 13 +line:22 col:3 --int--INT-- +token: line 22, column 3, sym: 5 +line:22 col:7 --intHex--IDENTIFIER-- +token: line 22, column 7, sym: 98, value: 'intHex' +line:22 col:14 --=--EQ-- +token: line 22, column 14, sym: 18 +line:22 col:16 --0x0001--INTEGER_LITERAL-- +token: line 22, column 16, sym: 93, value: '1' +line:22 col:22 --;--SEMICOLON-- +token: line 22, column 22, sym: 13 +line:23 col:3 --long--LONG-- +token: line 23, column 3, sym: 6 +line:23 col:8 --longHex--IDENTIFIER-- +token: line 23, column 8, sym: 98, value: 'longHex' +line:23 col:16 --=--EQ-- +token: line 23, column 16, sym: 18 +line:23 col:18 --0xFFFFl--INTEGER_LITERAL-- +token: line 23, column 18, sym: 93, value: '65535' +line:23 col:25 --;--SEMICOLON-- +token: line 23, column 25, sym: 13 +line:24 col:3 --int--INT-- +token: line 24, column 3, sym: 5 +line:24 col:7 --intOct--IDENTIFIER-- +token: line 24, column 7, sym: 98, value: 'intOct' +line:24 col:14 --=--EQ-- +token: line 24, column 14, sym: 18 +line:24 col:16 --0377--INTEGER_LITERAL-- +token: line 24, column 16, sym: 93, value: '255' +line:24 col:20 --;--SEMICOLON-- +token: line 24, column 20, sym: 13 +line:25 col:3 --long--LONG-- +token: line 25, column 3, sym: 6 +line:25 col:8 --longOc--IDENTIFIER-- +token: line 25, column 8, sym: 98, value: 'longOc' +line:25 col:15 --=--EQ-- +token: line 25, column 15, sym: 18 +line:25 col:17 --007l--INTEGER_LITERAL-- +token: line 25, column 17, sym: 93, value: '7' +line:25 col:21 --;--SEMICOLON-- +token: line 25, column 21, sym: 13 +line:26 col:3 --int--INT-- +token: line 26, column 3, sym: 5 +line:26 col:7 --smallest--IDENTIFIER-- +token: line 26, column 7, sym: 98, value: 'smallest' +line:26 col:16 --=--EQ-- +token: line 26, column 16, sym: 18 +line:26 col:18 ---2147483648--INTEGER_LITERAL-- +token: line 26, column 18, sym: 93, value: '-2147483648' +line:26 col:29 --;--SEMICOLON-- +token: line 26, column 29, sym: 13 +line:28 col:3 --public--PUBLIC-- +token: line 28, column 3, sym: 24 +line:28 col:10 --static--STATIC-- +token: line 28, column 10, sym: 27 +line:28 col:17 --void--VOID-- +token: line 28, column 17, sym: 37 +line:28 col:22 --main--IDENTIFIER-- +token: line 28, column 22, sym: 98, value: 'main' +line:28 col:26 --(--LPAREN-- +token: line 28, column 26, sym: 19 +line:28 col:27 --String--IDENTIFIER-- +token: line 28, column 27, sym: 98, value: 'String' +line:28 col:34 --argv--IDENTIFIER-- +token: line 28, column 34, sym: 98, value: 'argv' +line:28 col:38 --[--LBRACK-- +token: line 28, column 38, sym: 10 +line:28 col:39 --]--RBRACK-- +token: line 28, column 39, sym: 11 +line:28 col:40 --)--RPAREN-- +token: line 28, column 40, sym: 20 +line:28 col:42 --{--LBRACE-- +token: line 28, column 42, sym: 16 +line:30 col:5 --for--FOR-- +token: line 30, column 5, sym: 49 +line:30 col:9 --(--LPAREN-- +token: line 30, column 9, sym: 19 +line:30 col:10 --int--INT-- +token: line 30, column 10, sym: 5 +line:30 col:14 --i--IDENTIFIER-- +token: line 30, column 14, sym: 98, value: 'i' +line:30 col:16 --=--EQ-- +token: line 30, column 16, sym: 18 +line:30 col:18 --0--INTEGER_LITERAL-- +token: line 30, column 18, sym: 93, value: '0' +line:30 col:19 --;--SEMICOLON-- +token: line 30, column 19, sym: 13 +line:30 col:21 --i--IDENTIFIER-- +token: line 30, column 21, sym: 98, value: 'i' +line:30 col:23 --<--LT-- +token: line 30, column 23, sym: 69 +line:30 col:25 --argv--IDENTIFIER-- +token: line 30, column 25, sym: 98, value: 'argv' +line:30 col:29 --.--DOT-- +token: line 30, column 29, sym: 12 +line:30 col:30 --length--IDENTIFIER-- +token: line 30, column 30, sym: 98, value: 'length' +line:30 col:36 --;--SEMICOLON-- +token: line 30, column 36, sym: 13 +line:30 col:38 --i--IDENTIFIER-- +token: line 30, column 38, sym: 98, value: 'i' +line:30 col:39 --++--PLUSPLUS-- +token: line 30, column 39, sym: 58 +line:30 col:41 --)--RPAREN-- +token: line 30, column 41, sym: 20 +line:30 col:43 --{--LBRACE-- +token: line 30, column 43, sym: 16 +line:31 col:7 --try--TRY-- +token: line 31, column 7, sym: 54 +line:31 col:11 --{--LBRACE-- +token: line 31, column 11, sym: 16 +line:32 col:9 --System--IDENTIFIER-- +token: line 32, column 9, sym: 98, value: 'System' +line:32 col:15 --.--DOT-- +token: line 32, column 15, sym: 12 +line:32 col:16 --out--IDENTIFIER-- +token: line 32, column 16, sym: 98, value: 'out' +line:32 col:19 --.--DOT-- +token: line 32, column 19, sym: 12 +line:32 col:20 --println--IDENTIFIER-- +token: line 32, column 20, sym: 98, value: 'println' +line:32 col:27 --(--LPAREN-- +token: line 32, column 27, sym: 19 +line:32 col:37 --"--STRING_LITERAL-- +token: line 32, column 37, sym: 97, value: 'Lexing [' +line:32 col:39 --+--PLUS-- +token: line 32, column 39, sym: 60 +line:32 col:41 --argv--IDENTIFIER-- +token: line 32, column 41, sym: 98, value: 'argv' +line:32 col:45 --[--LBRACK-- +token: line 32, column 45, sym: 10 +line:32 col:46 --i--IDENTIFIER-- +token: line 32, column 46, sym: 98, value: 'i' +line:32 col:47 --]--RBRACK-- +token: line 32, column 47, sym: 11 +line:32 col:49 --+--PLUS-- +token: line 32, column 49, sym: 60 +line:32 col:53 --"--STRING_LITERAL-- +token: line 32, column 53, sym: 97, value: ']' +line:32 col:54 --)--RPAREN-- +token: line 32, column 54, sym: 20 +line:32 col:55 --;--SEMICOLON-- +token: line 32, column 55, sym: 13 +line:33 col:9 --Scanner--IDENTIFIER-- +token: line 33, column 9, sym: 98, value: 'Scanner' +line:33 col:17 --scanner--IDENTIFIER-- +token: line 33, column 17, sym: 98, value: 'scanner' +line:33 col:25 --=--EQ-- +token: line 33, column 25, sym: 18 +line:33 col:27 --new--NEW-- +token: line 33, column 27, sym: 57 +line:33 col:31 --Scanner--IDENTIFIER-- +token: line 33, column 31, sym: 98, value: 'Scanner' +line:33 col:38 --(--LPAREN-- +token: line 33, column 38, sym: 19 +line:33 col:39 --new--NEW-- +token: line 33, column 39, sym: 57 +line:33 col:43 --UnicodeEscapes--IDENTIFIER-- +token: line 33, column 43, sym: 98, value: 'UnicodeEscapes' +line:33 col:57 --(--LPAREN-- +token: line 33, column 57, sym: 19 +line:33 col:58 --new--NEW-- +token: line 33, column 58, sym: 57 +line:33 col:62 --FileReader--IDENTIFIER-- +token: line 33, column 62, sym: 98, value: 'FileReader' +line:33 col:72 --(--LPAREN-- +token: line 33, column 72, sym: 19 +line:33 col:73 --argv--IDENTIFIER-- +token: line 33, column 73, sym: 98, value: 'argv' +line:33 col:77 --[--LBRACK-- +token: line 33, column 77, sym: 10 +line:33 col:78 --i--IDENTIFIER-- +token: line 33, column 78, sym: 98, value: 'i' +line:33 col:79 --]--RBRACK-- +token: line 33, column 79, sym: 11 +line:33 col:80 --)--RPAREN-- +token: line 33, column 80, sym: 20 +line:33 col:81 --)--RPAREN-- +token: line 33, column 81, sym: 20 +line:33 col:82 --)--RPAREN-- +token: line 33, column 82, sym: 20 +line:33 col:83 --;--SEMICOLON-- +token: line 33, column 83, sym: 13 +line:35 col:9 --Symbol--IDENTIFIER-- +token: line 35, column 9, sym: 98, value: 'Symbol' +line:35 col:16 --s--IDENTIFIER-- +token: line 35, column 16, sym: 98, value: 's' +line:35 col:17 --;--SEMICOLON-- +token: line 35, column 17, sym: 13 +line:36 col:9 --do--DO-- +token: line 36, column 9, sym: 47 +line:36 col:12 --{--LBRACE-- +token: line 36, column 12, sym: 16 +line:37 col:11 --s--IDENTIFIER-- +token: line 37, column 11, sym: 98, value: 's' +line:37 col:13 --=--EQ-- +token: line 37, column 13, sym: 18 +line:37 col:15 --scanner--IDENTIFIER-- +token: line 37, column 15, sym: 98, value: 'scanner' +line:37 col:22 --.--DOT-- +token: line 37, column 22, sym: 12 +line:37 col:23 --debug_next_token--IDENTIFIER-- +token: line 37, column 23, sym: 98, value: 'debug_next_token' +line:37 col:39 --(--LPAREN-- +token: line 37, column 39, sym: 19 +line:37 col:40 --)--RPAREN-- +token: line 37, column 40, sym: 20 +line:37 col:41 --;--SEMICOLON-- +token: line 37, column 41, sym: 13 +line:38 col:11 --System--IDENTIFIER-- +token: line 38, column 11, sym: 98, value: 'System' +line:38 col:17 --.--DOT-- +token: line 38, column 17, sym: 12 +line:38 col:18 --out--IDENTIFIER-- +token: line 38, column 18, sym: 98, value: 'out' +line:38 col:21 --.--DOT-- +token: line 38, column 21, sym: 12 +line:38 col:22 --println--IDENTIFIER-- +token: line 38, column 22, sym: 98, value: 'println' +line:38 col:29 --(--LPAREN-- +token: line 38, column 29, sym: 19 +line:38 col:38 --"--STRING_LITERAL-- +token: line 38, column 38, sym: 97, value: 'token: ' +line:38 col:40 --+--PLUS-- +token: line 38, column 40, sym: 60 +line:38 col:42 --s--IDENTIFIER-- +token: line 38, column 42, sym: 98, value: 's' +line:38 col:43 --)--RPAREN-- +token: line 38, column 43, sym: 20 +line:38 col:44 --;--SEMICOLON-- +token: line 38, column 44, sym: 13 +line:39 col:9 --}--RBRACE-- +token: line 39, column 9, sym: 17 +line:39 col:11 --while--WHILE-- +token: line 39, column 11, sym: 48 +line:39 col:17 --(--LPAREN-- +token: line 39, column 17, sym: 19 +line:39 col:18 --s--IDENTIFIER-- +token: line 39, column 18, sym: 98, value: 's' +line:39 col:19 --.--DOT-- +token: line 39, column 19, sym: 12 +line:39 col:20 --sym--IDENTIFIER-- +token: line 39, column 20, sym: 98, value: 'sym' +line:39 col:24 --!=--NOTEQ-- +token: line 39, column 24, sym: 75 +line:39 col:27 --sym--IDENTIFIER-- +token: line 39, column 27, sym: 98, value: 'sym' +line:39 col:30 --.--DOT-- +token: line 39, column 30, sym: 12 +line:39 col:31 --EOF--IDENTIFIER-- +token: line 39, column 31, sym: 98, value: 'EOF' +line:39 col:34 --)--RPAREN-- +token: line 39, column 34, sym: 20 +line:39 col:35 --;--SEMICOLON-- +token: line 39, column 35, sym: 13 +line:41 col:9 --System--IDENTIFIER-- +token: line 41, column 9, sym: 98, value: 'System' +line:41 col:15 --.--DOT-- +token: line 41, column 15, sym: 12 +line:41 col:16 --out--IDENTIFIER-- +token: line 41, column 16, sym: 98, value: 'out' +line:41 col:19 --.--DOT-- +token: line 41, column 19, sym: 12 +line:41 col:20 --println--IDENTIFIER-- +token: line 41, column 20, sym: 98, value: 'println' +line:41 col:27 --(--LPAREN-- +token: line 41, column 27, sym: 19 +line:41 col:39 --"--STRING_LITERAL-- +token: line 41, column 39, sym: 97, value: 'No errors.' +line:41 col:40 --)--RPAREN-- +token: line 41, column 40, sym: 20 +line:41 col:41 --;--SEMICOLON-- +token: line 41, column 41, sym: 13 +line:42 col:7 --}--RBRACE-- +token: line 42, column 7, sym: 17 +line:42 col:9 --catch--CATCH-- +token: line 42, column 9, sym: 55 +line:42 col:15 --(--LPAREN-- +token: line 42, column 15, sym: 19 +line:42 col:16 --Exception--IDENTIFIER-- +token: line 42, column 16, sym: 98, value: 'Exception' +line:42 col:26 --e--IDENTIFIER-- +token: line 42, column 26, sym: 98, value: 'e' +line:42 col:27 --)--RPAREN-- +token: line 42, column 27, sym: 20 +line:42 col:29 --{--LBRACE-- +token: line 42, column 29, sym: 16 +line:43 col:9 --e--IDENTIFIER-- +token: line 43, column 9, sym: 98, value: 'e' +line:43 col:10 --.--DOT-- +token: line 43, column 10, sym: 12 +line:43 col:11 --printStackTrace--IDENTIFIER-- +token: line 43, column 11, sym: 98, value: 'printStackTrace' +line:43 col:26 --(--LPAREN-- +token: line 43, column 26, sym: 19 +line:43 col:27 --System--IDENTIFIER-- +token: line 43, column 27, sym: 98, value: 'System' +line:43 col:33 --.--DOT-- +token: line 43, column 33, sym: 12 +line:43 col:34 --out--IDENTIFIER-- +token: line 43, column 34, sym: 98, value: 'out' +line:43 col:37 --)--RPAREN-- +token: line 43, column 37, sym: 20 +line:43 col:38 --;--SEMICOLON-- +token: line 43, column 38, sym: 13 +line:44 col:9 --System--IDENTIFIER-- +token: line 44, column 9, sym: 98, value: 'System' +line:44 col:15 --.--DOT-- +token: line 44, column 15, sym: 12 +line:44 col:16 --exit--IDENTIFIER-- +token: line 44, column 16, sym: 98, value: 'exit' +line:44 col:20 --(--LPAREN-- +token: line 44, column 20, sym: 19 +line:44 col:21 --1--INTEGER_LITERAL-- +token: line 44, column 21, sym: 93, value: '1' +line:44 col:22 --)--RPAREN-- +token: line 44, column 22, sym: 20 +line:44 col:23 --;--SEMICOLON-- +token: line 44, column 23, sym: 13 +line:45 col:7 --}--RBRACE-- +token: line 45, column 7, sym: 17 +line:46 col:5 --}--RBRACE-- +token: line 46, column 5, sym: 17 +line:47 col:3 --}--RBRACE-- +token: line 47, column 3, sym: 17 +line:48 col:1 --}--RBRACE-- +token: line 48, column 1, sym: 17 +line:49 col:1 ----EOF-- +token: line 49, column 1, sym: 0 +No errors. diff --git a/jflex/examples/cup-maven/README.md b/jflex/examples/cup-lcalc/README.md similarity index 90% rename from jflex/examples/cup-maven/README.md rename to jflex/examples/cup-lcalc/README.md index 4fb9feeb5..16d5552ca 100644 --- a/jflex/examples/cup-maven/README.md +++ b/jflex/examples/cup-lcalc/README.md @@ -1,5 +1,4 @@ -JFlex with cup -============== +# JFlex with cup This directory contains a small example of integration between JFlex and [CUP][cup]. @@ -10,7 +9,9 @@ titled _Compiler Construction Tools_. Small changes and updates to newest JFlex+Cup versions by Gerwin Klein. -## Build and run +## Build, run, test + +### Using Maven To compile: @@ -25,10 +26,10 @@ To run: ``` mvn package -java -jar target/simple-maven-full-1.0.jar Main test.txt +java -jar target/cup-lcalc-full-1.0.jar Main test.txt ``` -## Files: +## Files * `src/main/java/Main.java` demo of a main program * `src/main/flex/lcalc.flex` the lexer spec diff --git a/jflex/examples/cup-maven/pom.xml b/jflex/examples/cup-lcalc/pom.xml similarity index 86% rename from jflex/examples/cup-maven/pom.xml rename to jflex/examples/cup-lcalc/pom.xml index 975da5991..e7709d32c 100644 --- a/jflex/examples/cup-maven/pom.xml +++ b/jflex/examples/cup-lcalc/pom.xml @@ -18,6 +18,12 @@ 3.8.2 test + + com.google.truth + truth + 0.36 + test + de.jflex cup_runtime @@ -26,6 +32,15 @@ + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.0 + + 1.6 + 1.6 + + de.jflex jflex-maven-plugin @@ -36,7 +51,6 @@ generate - true diff --git a/jflex/examples/cup-maven/src/main/cup/ycalc.cup b/jflex/examples/cup-lcalc/src/main/cup/ycalc.cup similarity index 100% rename from jflex/examples/cup-maven/src/main/cup/ycalc.cup rename to jflex/examples/cup-lcalc/src/main/cup/ycalc.cup diff --git a/jflex/examples/cup-maven/src/main/java/Main.java b/jflex/examples/cup-lcalc/src/main/java/Main.java similarity index 52% rename from jflex/examples/cup-maven/src/main/java/Main.java rename to jflex/examples/cup-lcalc/src/main/java/Main.java index a0529ae77..c3253c7b4 100644 --- a/jflex/examples/cup-maven/src/main/java/Main.java +++ b/jflex/examples/cup-lcalc/src/main/java/Main.java @@ -1,39 +1,35 @@ /* - This example comes from a short article series in the Linux + This example comes from a short article series in the Linux Gazette by Richard A. Sevenich and Christopher Lopes, titled "Compiler Construction Tools". The article series starts at http://www.linuxgazette.com/issue39/sevenich.html - Small changes and updates to newest JFlex+Cup versions + Small changes and updates to newest JFlex+Cup versions by Gerwin Klein */ /* Commented By: Christopher Lopes - File Name: Main.java - To Create: - After the scanner, lcalc.flex, and the parser, ycalc.cup, have been created. - > javac Main.java - - To Run: - > java Main test.txt - where test.txt is an test input file for the calculator. + +

To Run: +

+  java Main test.txt
+  
+ where {@code test.txt} is an test input file for the calculator. */ - + import java.io.*; - + public class Main { - static public void main(String argv[]) { + public static void main(String argv[]) { /* Start the parser */ try { parser p = new parser(new Lexer(new FileReader(argv[0]))); - Object result = p.parse().value; + Object result = p.parse().value; } catch (Exception e) { /* do cleanup here -- possibly rethrow e */ e.printStackTrace(); } } } - - diff --git a/jflex/examples/cup-maven/src/main/jflex/lcalc.flex b/jflex/examples/cup-lcalc/src/main/jflex/lcalc.flex similarity index 100% rename from jflex/examples/cup-maven/src/main/jflex/lcalc.flex rename to jflex/examples/cup-lcalc/src/main/jflex/lcalc.flex diff --git a/jflex/examples/cup-maven/src/test/java/LexerTest.java b/jflex/examples/cup-lcalc/src/test/java/LexerTest.java similarity index 80% rename from jflex/examples/cup-maven/src/test/java/LexerTest.java rename to jflex/examples/cup-lcalc/src/test/java/LexerTest.java index 51d27533c..c09d45082 100644 --- a/jflex/examples/cup-maven/src/test/java/LexerTest.java +++ b/jflex/examples/cup-lcalc/src/test/java/LexerTest.java @@ -1,10 +1,12 @@ +import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth.assertWithMessage; + import java.io.BufferedReader; import java.io.File; import java.io.FileOutputStream; import java.io.FileReader; import java.io.IOException; import java.io.PrintStream; - import junit.framework.TestCase; /** @@ -16,9 +18,7 @@ public class LexerTest extends TestCase { private static final String OUTPUT_FILE = "target/output.actual"; - /** - * Tests that Lexer parses the calculator syntax correctly. - */ + /** Tests that Lexer parses the calculator syntax correctly. */ public void testOutput() throws Exception { String[] argv = new String[1]; argv[0] = "src/test/resources/test.txt"; @@ -40,8 +40,8 @@ public void testOutput() throws Exception { } private void checkFileContent(File expected, File actual) throws IOException { - assertTrue(expected.isFile()); - assertTrue(actual.isFile()); + assertThat(expected.isFile()).isTrue(); + assertThat(actual.isFile()).isTrue(); BufferedReader actualContent = new BufferedReader(new FileReader(actual)); BufferedReader expectedContent = new BufferedReader(new FileReader(expected)); @@ -49,7 +49,7 @@ private void checkFileContent(File expected, File actual) throws IOException { for (int lineNumber = 1; lineNumber != -1; lineNumber++) { String expectedLine = expectedContent.readLine(); String actualLine = actualContent.readLine(); - assertEquals("Line " + lineNumber, expectedLine, actualLine); + assertWithMessage("Line " + lineNumber).that(actualLine).isEqualTo(expectedLine); if (expectedLine == null) { lineNumber = -2; // EOF } diff --git a/jflex/examples/cup-maven/src/test/resources/output.good b/jflex/examples/cup-lcalc/src/test/resources/output.good similarity index 100% rename from jflex/examples/cup-maven/src/test/resources/output.good rename to jflex/examples/cup-lcalc/src/test/resources/output.good diff --git a/jflex/examples/cup-maven/src/test/resources/test.txt b/jflex/examples/cup-lcalc/src/test/resources/test.txt similarity index 100% rename from jflex/examples/cup-maven/src/test/resources/test.txt rename to jflex/examples/cup-lcalc/src/test/resources/test.txt diff --git a/jflex/examples/interpreter/Main.java b/jflex/examples/interpreter/Main.java deleted file mode 100644 index dfb47be38..000000000 --- a/jflex/examples/interpreter/Main.java +++ /dev/null @@ -1,74 +0,0 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Copyright (C) 2001 Gerwin Klein * - * Copyright (C) 2001 Bernhard Rumpe * - * All rights reserved. * - * * - * License: BSD * - * * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - - -import java.io.*; - -/** - * Main program of the interpreter for the AS programming language. - * Based on JFlex/CUP. - * - * Steps: - * - scanning (Yylex) - * - context free parsing and AST building (yyparse) - * - build up symbol table (setSymtabs) - * - check context conditions (checkcontext) - * - prepare interpretation (prepInterp) - * - start interpretation (interpret) - */ -public class Main { - - public static void main(String [] args) throws Exception { - Reader reader = null; - - if (args.length == 1) { - File input = new File(args[0]); - if (!input.canRead()) { - System.out.println("Error: could not read ["+input+"]"); - } - reader = new FileReader(input); - } - else { - reader = new InputStreamReader(System.in); - } - - Yylex scanner = new Yylex(reader); // create scanner - SymTab symtab = new SymTab(); // set global symbol table - scanner.setSymtab(symtab); - - parser parser = new parser(scanner); // create parser - Tprogram syntaxbaum = null; - - try { - syntaxbaum = (Tprogram) parser.parse().value; // parse - } - catch (Exception e) { - e.printStackTrace(); - } - - // System.out.println(symtab); - System.out.println(syntaxbaum); - - syntaxbaum.setSymtabs(); // set symbol table - // syntaxbaum.printSymtabs(); // print symbol table - - syntaxbaum.checkcontext(); // CoCo (DefVar, DefFun, Arity) - if(contexterror>0) return; - - syntaxbaum.prepInterp(); // var. indices and function pointers - // im Syntaxbaum setzen - syntaxbaum.interpret(); // interpretation - } - - static int contexterror = 0; // number of errors in context conditions - - public static void error(String s) { - System.out.println((contexterror++)+". "+s); - } -} diff --git a/jflex/examples/interpreter/Makefile b/jflex/examples/interpreter/Makefile deleted file mode 100644 index 5f0b3d880..000000000 --- a/jflex/examples/interpreter/Makefile +++ /dev/null @@ -1,56 +0,0 @@ -# uses JFlex >= 1.3.2, and CUP >= 0.10j -# -# targets: -# -# make all -# generates lexer, and parser, and compiles all *.java files -# -# make run (or just: make) -# starts the program on a test example -# - -JAVA=java -JAVAC=javac -# Root of the project -ROOT=../../.. -JFLEX=$(ROOT)/jflex/bin/jflex -CUPJAR=$(ROOT)/cup/cup/java-cup-11b.jar -CUP=$(JAVA) -jar $(CUPJAR) -CP=.:$(CUPJAR) - -default: test - -.SUFFIXES: $(SUFFIXES) .class .java - -.java.class: - $(JAVAC) -cp $(CP) $*.java - -FILE= Yylex.java parser.java sym.java \ - SymTab.java AST.java \ - Tnumber.java Tident.java Texp.java \ - Tfun.java Texpinfix.java Tuminus.java \ - Tboolexp.java Tifthenelse.java \ - Tdekl.java Tdekllist.java \ - Tparlist.java Texplist.java Tprogram.java \ - Main.java \ - SymtabEntry.java STEfun.java STEvar.java - -run: output.txt - -output.txt: all - $(JAVA) -cp $(CP) Main < example.as > output.txt - cat output.txt - -all: Yylex.java parser.java $(FILE:java=class) - -clean: - rm -f *.class *~ *.bak Yylex.java parser.java sym.java - -Yylex.java: scanner.flex - $(JFLEX) scanner.flex - -parser.java: parser.cup - $(CUP) -interface < parser.cup - -test: output.txt - @(diff output.txt output.good && echo "Test OK!") || echo "Test failed!" diff --git a/jflex/examples/interpreter/Tdekl.java b/jflex/examples/interpreter/Tdekl.java deleted file mode 100644 index 60a474b84..000000000 --- a/jflex/examples/interpreter/Tdekl.java +++ /dev/null @@ -1,64 +0,0 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Copyright (C) 2001 Gerwin Klein * - * Copyright (C) 2001 Bernhard Rumpe * - * All rights reserved. * - * * - * License: BSD * - * * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - - -/** - * AST node for function declarations. - * - * Also contains a reference to the symbol table of - * the parameters and their arity. - */ -class Tdekl implements AST { - Tident ident; // identifier - Tparlist parlist; // liste of parameter - Texp exp; // function body - - public Tdekl(Tident i, Tparlist p, Texp e) { - parlist=p; - ident=i; - exp=e; - } - - public String toString() { - return(ident+"("+parlist+") = \n "+exp); - } - - SymTab params; // symbol table of the parameters - int arity; - - public void setSymtab(SymTab st) { - params = new SymTab(st); - parlist.setSymtab(params,false,0); - arity = params.size(); - - boolean isNew = st.enter(ident.toString(), - new STEfun(ident.toString(),this,arity)); - // CoCo (Fun) - if(!isNew) Main.error("funktion "+ident+" defined twice!"); - } - - public void printSymtabs() { - System.out.print("funktion "+ident.toString()+"\n"+params); - } - - public void checkcontext() { - exp.checkcontext(params); // CoCo (DefFun,DefVar,Arity) - } - - public void prepInterp(SymTab st) { // set pointers and indices - exp.prepInterp(params); - } - - public int interpret(int[] in, int[] par) { - return(exp.interpret(in,par)); - } - - public int arity() { return(arity); } -} - diff --git a/jflex/examples/interpreter/Tprogram.java b/jflex/examples/interpreter/Tprogram.java deleted file mode 100644 index e663b1a77..000000000 --- a/jflex/examples/interpreter/Tprogram.java +++ /dev/null @@ -1,88 +0,0 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Copyright (C) 2001 Gerwin Klein * - * Copyright (C) 2001 Bernhard Rumpe * - * All rights reserved. * - * * - * License: BSD * - * * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - - -/** - * AST node for the whole program (top node). - * - * Also contains two symbol tables, one for input variables, - * one for function names. - * - * All operations like context check, symbol table build up - * etc. start here. - */ -class Tprogram implements AST { - - Tparlist parlist; // input variables - Tdekllist dekllist; // function declarations - Texplist explist; // result expressions - Texplist arguments; // input values - - public Tprogram(Tparlist p, Tdekllist d, Texplist e, Texplist a) { - parlist=p; - dekllist=d; - explist=e; - arguments=a; - } - - public String toString() { - return("Program:\n=============\ninput "+parlist+ - "\nfunctions\n"+dekllist+"\noutput "+explist+ - "\narguments "+arguments+"\nend"); - } - - SymTab inputs; // table of input variables - SymTab functions; // table of functions - - public void setSymtabs() { // calculate symbol table entries - inputs = new SymTab(); // set input variables - parlist.setSymtab(inputs, true, 0); - functions = new SymTab(inputs); - dekllist.setSymtab(functions); - } - - public void printSymtabs() { - System.out.print("Input variables-\n"+inputs); - System.out.print("Functions-\n"+functions); - dekllist.printSymtabs(); - } - - public void checkcontext() { - dekllist.checkcontext(); // CoCo (DefFun,DefVar,Arity) - // in function bodies - explist.checkcontext(functions); // CoCo (DefFun,DefVar,Arity) - // in result expressions - arguments.checkcontext(new SymTab()); // CoCo (constants) - // in arguments - if (arguments.length()!=inputs.size()) - Main.error("Argument list and input variables list differ!"); - } - - public void prepInterp() { // set pointers and indices - dekllist.prepInterp(functions); - explist.prepInterp(functions); - } - - public void interpret() { - int[] inputEnv = new int[inputs.size()]; // set input - - arguments.interpret(null,null,inputEnv,0); - - System.out.println("Result:\n============="); - - int[] ergebnis = new int[explist.length()]; - explist.interpret(inputEnv,null,ergebnis,0); // calculate result - - int i; - for (i=explist.length()-1; i > 0; i--) - System.out.print(ergebnis[i]+", "); - System.out.println(ergebnis[i]); - } -} - diff --git a/jflex/examples/interpreter/build.xml b/jflex/examples/interpreter/build.xml deleted file mode 100644 index e8e78dbf2..000000000 --- a/jflex/examples/interpreter/build.xml +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/jflex/examples/java/Makefile b/jflex/examples/java/Makefile deleted file mode 100644 index 40b27856b..000000000 --- a/jflex/examples/java/Makefile +++ /dev/null @@ -1,54 +0,0 @@ -# You need CUP v0.10j (or newer) for this makefile to work (for java12.cup) -# -# CUP classes should be included in CLASSPATH - -CUP = java java_cup.Main -interface < -# Root of the project -ROOT = ../../.. -JFLEX = $(ROOT)/jflex/bin/jflex -CUPJAR = $(ROOT)/cup/cup/java-cup-11b.jar -CP = .:$(CUPJAR) -JAVA = java -JAVAC = javac -JAVACFLAGS = -cp $(CP) -CUP = $(JAVA) -jar $(CUPJAR) - - - -# -------------------------------------------------- - -all: test - -test: lexer-output.txt - @(diff lexer-output.txt lexer-output.good && echo "Test OK!") || echo "Test failed!" - -lexer-output.txt: compile - $(JAVA) -cp $(CP) TestLexer TestLexer.java > lexer-output.txt - - -compile: scanner parser unicode - $(JAVAC) $(JAVACFLAGS) JavaParser.java TestLexer.java - -parser: parser.java - -parser.java: java12.cup - $(CUP) -interface java12.cup - -scanner: Scanner.java - -Scanner.java: java.flex - $(JFLEX) java.flex - -unicode: UnicodeEscapes.java - -UnicodeEscapes.java: unicode.flex - $(JFLEX) unicode.flex - -clean: - rm -f *.class - rm -f *~ - rm -f Scanner.java - rm -f parser.java - rm -f sym.java - rm -f UnicodeEscapes.java - rm -f lexer-output.txt diff --git a/jflex/examples/java/build.xml b/jflex/examples/java/build.xml deleted file mode 100644 index 96ba39aad..000000000 --- a/jflex/examples/java/build.xml +++ /dev/null @@ -1,46 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/jflex/examples/java/lexer-output.good b/jflex/examples/java/lexer-output.good deleted file mode 100644 index 7a70ebc34..000000000 --- a/jflex/examples/java/lexer-output.good +++ /dev/null @@ -1,386 +0,0 @@ -Lexing [TestLexer.java] -line:9 col:1 --import--IMPORT-- -token: line 9, column 1, sym: 23 -line:9 col:8 --java--IDENTIFIER-- -token: line 9, column 8, sym: 98, value: 'java' -line:9 col:12 --.--DOT-- -token: line 9, column 12, sym: 12 -line:9 col:13 --io--IDENTIFIER-- -token: line 9, column 13, sym: 98, value: 'io' -line:9 col:15 --.--DOT-- -token: line 9, column 15, sym: 12 -line:9 col:16 --*--MULT-- -token: line 9, column 16, sym: 14 -line:9 col:17 --;--SEMICOLON-- -token: line 9, column 17, sym: 13 -line:10 col:1 --import--IMPORT-- -token: line 10, column 1, sym: 23 -line:10 col:8 --java_cup--IDENTIFIER-- -token: line 10, column 8, sym: 98, value: 'java_cup' -line:10 col:16 --.--DOT-- -token: line 10, column 16, sym: 12 -line:10 col:17 --runtime--IDENTIFIER-- -token: line 10, column 17, sym: 98, value: 'runtime' -line:10 col:24 --.--DOT-- -token: line 10, column 24, sym: 12 -line:10 col:25 --Symbol--IDENTIFIER-- -token: line 10, column 25, sym: 98, value: 'Symbol' -line:10 col:31 --;--SEMICOLON-- -token: line 10, column 31, sym: 13 -line:18 col:1 --public--PUBLIC-- -token: line 18, column 1, sym: 24 -line:18 col:8 --class--CLASS-- -token: line 18, column 8, sym: 34 -line:18 col:14 --TestLexer--IDENTIFIER-- -token: line 18, column 14, sym: 98, value: 'TestLexer' -line:18 col:24 --{--LBRACE-- -token: line 18, column 24, sym: 16 -line:21 col:3 --int--INT-- -token: line 21, column 3, sym: 5 -line:21 col:7 --intDec--IDENTIFIER-- -token: line 21, column 7, sym: 98, value: 'intDec' -line:21 col:14 --=--EQ-- -token: line 21, column 14, sym: 18 -line:21 col:16 --37--INTEGER_LITERAL-- -token: line 21, column 16, sym: 93, value: '37' -line:21 col:18 --;--SEMICOLON-- -token: line 21, column 18, sym: 13 -line:22 col:3 --long--LONG-- -token: line 22, column 3, sym: 6 -line:22 col:8 --longDec--IDENTIFIER-- -token: line 22, column 8, sym: 98, value: 'longDec' -line:22 col:16 --=--EQ-- -token: line 22, column 16, sym: 18 -line:22 col:18 --37l--INTEGER_LITERAL-- -token: line 22, column 18, sym: 93, value: '37' -line:22 col:21 --;--SEMICOLON-- -token: line 22, column 21, sym: 13 -line:23 col:3 --int--INT-- -token: line 23, column 3, sym: 5 -line:23 col:7 --intHex--IDENTIFIER-- -token: line 23, column 7, sym: 98, value: 'intHex' -line:23 col:14 --=--EQ-- -token: line 23, column 14, sym: 18 -line:23 col:16 --0x0001--INTEGER_LITERAL-- -token: line 23, column 16, sym: 93, value: '1' -line:23 col:22 --;--SEMICOLON-- -token: line 23, column 22, sym: 13 -line:24 col:3 --long--LONG-- -token: line 24, column 3, sym: 6 -line:24 col:8 --longHex--IDENTIFIER-- -token: line 24, column 8, sym: 98, value: 'longHex' -line:24 col:16 --=--EQ-- -token: line 24, column 16, sym: 18 -line:24 col:18 --0xFFFFl--INTEGER_LITERAL-- -token: line 24, column 18, sym: 93, value: '65535' -line:24 col:25 --;--SEMICOLON-- -token: line 24, column 25, sym: 13 -line:25 col:3 --int--INT-- -token: line 25, column 3, sym: 5 -line:25 col:7 --intOct--IDENTIFIER-- -token: line 25, column 7, sym: 98, value: 'intOct' -line:25 col:14 --=--EQ-- -token: line 25, column 14, sym: 18 -line:25 col:16 --0377--INTEGER_LITERAL-- -token: line 25, column 16, sym: 93, value: '255' -line:25 col:20 --;--SEMICOLON-- -token: line 25, column 20, sym: 13 -line:26 col:3 --long--LONG-- -token: line 26, column 3, sym: 6 -line:26 col:8 --longOc--IDENTIFIER-- -token: line 26, column 8, sym: 98, value: 'longOc' -line:26 col:15 --=--EQ-- -token: line 26, column 15, sym: 18 -line:26 col:17 --007l--INTEGER_LITERAL-- -token: line 26, column 17, sym: 93, value: '7' -line:26 col:21 --;--SEMICOLON-- -token: line 26, column 21, sym: 13 -line:27 col:3 --int--INT-- -token: line 27, column 3, sym: 5 -line:27 col:7 --smallest--IDENTIFIER-- -token: line 27, column 7, sym: 98, value: 'smallest' -line:27 col:16 --=--EQ-- -token: line 27, column 16, sym: 18 -line:27 col:18 ---2147483648--INTEGER_LITERAL-- -token: line 27, column 18, sym: 93, value: '-2147483648' -line:27 col:29 --;--SEMICOLON-- -token: line 27, column 29, sym: 13 -line:29 col:3 --public--PUBLIC-- -token: line 29, column 3, sym: 24 -line:29 col:10 --static--STATIC-- -token: line 29, column 10, sym: 27 -line:29 col:17 --void--VOID-- -token: line 29, column 17, sym: 37 -line:29 col:22 --main--IDENTIFIER-- -token: line 29, column 22, sym: 98, value: 'main' -line:29 col:26 --(--LPAREN-- -token: line 29, column 26, sym: 19 -line:29 col:27 --String--IDENTIFIER-- -token: line 29, column 27, sym: 98, value: 'String' -line:29 col:34 --argv--IDENTIFIER-- -token: line 29, column 34, sym: 98, value: 'argv' -line:29 col:38 --[--LBRACK-- -token: line 29, column 38, sym: 10 -line:29 col:39 --]--RBRACK-- -token: line 29, column 39, sym: 11 -line:29 col:40 --)--RPAREN-- -token: line 29, column 40, sym: 20 -line:29 col:42 --{--LBRACE-- -token: line 29, column 42, sym: 16 -line:31 col:5 --for--FOR-- -token: line 31, column 5, sym: 49 -line:31 col:9 --(--LPAREN-- -token: line 31, column 9, sym: 19 -line:31 col:10 --int--INT-- -token: line 31, column 10, sym: 5 -line:31 col:14 --i--IDENTIFIER-- -token: line 31, column 14, sym: 98, value: 'i' -line:31 col:16 --=--EQ-- -token: line 31, column 16, sym: 18 -line:31 col:18 --0--INTEGER_LITERAL-- -token: line 31, column 18, sym: 93, value: '0' -line:31 col:19 --;--SEMICOLON-- -token: line 31, column 19, sym: 13 -line:31 col:21 --i--IDENTIFIER-- -token: line 31, column 21, sym: 98, value: 'i' -line:31 col:23 --<--LT-- -token: line 31, column 23, sym: 69 -line:31 col:25 --argv--IDENTIFIER-- -token: line 31, column 25, sym: 98, value: 'argv' -line:31 col:29 --.--DOT-- -token: line 31, column 29, sym: 12 -line:31 col:30 --length--IDENTIFIER-- -token: line 31, column 30, sym: 98, value: 'length' -line:31 col:36 --;--SEMICOLON-- -token: line 31, column 36, sym: 13 -line:31 col:38 --i--IDENTIFIER-- -token: line 31, column 38, sym: 98, value: 'i' -line:31 col:39 --++--PLUSPLUS-- -token: line 31, column 39, sym: 58 -line:31 col:41 --)--RPAREN-- -token: line 31, column 41, sym: 20 -line:31 col:43 --{--LBRACE-- -token: line 31, column 43, sym: 16 -line:32 col:7 --try--TRY-- -token: line 32, column 7, sym: 54 -line:32 col:11 --{--LBRACE-- -token: line 32, column 11, sym: 16 -line:33 col:9 --System--IDENTIFIER-- -token: line 33, column 9, sym: 98, value: 'System' -line:33 col:15 --.--DOT-- -token: line 33, column 15, sym: 12 -line:33 col:16 --out--IDENTIFIER-- -token: line 33, column 16, sym: 98, value: 'out' -line:33 col:19 --.--DOT-- -token: line 33, column 19, sym: 12 -line:33 col:20 --println--IDENTIFIER-- -token: line 33, column 20, sym: 98, value: 'println' -line:33 col:27 --(--LPAREN-- -token: line 33, column 27, sym: 19 -line:33 col:37 --"--STRING_LITERAL-- -token: line 33, column 37, sym: 97, value: 'Lexing [' -line:33 col:38 --+--PLUS-- -token: line 33, column 38, sym: 60 -line:33 col:39 --argv--IDENTIFIER-- -token: line 33, column 39, sym: 98, value: 'argv' -line:33 col:43 --[--LBRACK-- -token: line 33, column 43, sym: 10 -line:33 col:44 --i--IDENTIFIER-- -token: line 33, column 44, sym: 98, value: 'i' -line:33 col:45 --]--RBRACK-- -token: line 33, column 45, sym: 11 -line:33 col:46 --+--PLUS-- -token: line 33, column 46, sym: 60 -line:33 col:49 --"--STRING_LITERAL-- -token: line 33, column 49, sym: 97, value: ']' -line:33 col:50 --)--RPAREN-- -token: line 33, column 50, sym: 20 -line:33 col:51 --;--SEMICOLON-- -token: line 33, column 51, sym: 13 -line:34 col:9 --Scanner--IDENTIFIER-- -token: line 34, column 9, sym: 98, value: 'Scanner' -line:34 col:17 --scanner--IDENTIFIER-- -token: line 34, column 17, sym: 98, value: 'scanner' -line:34 col:25 --=--EQ-- -token: line 34, column 25, sym: 18 -line:34 col:27 --new--NEW-- -token: line 34, column 27, sym: 57 -line:34 col:31 --Scanner--IDENTIFIER-- -token: line 34, column 31, sym: 98, value: 'Scanner' -line:34 col:38 --(--LPAREN-- -token: line 34, column 38, sym: 19 -line:34 col:39 --new--NEW-- -token: line 34, column 39, sym: 57 -line:34 col:43 --UnicodeEscapes--IDENTIFIER-- -token: line 34, column 43, sym: 98, value: 'UnicodeEscapes' -line:34 col:57 --(--LPAREN-- -token: line 34, column 57, sym: 19 -line:34 col:58 --new--NEW-- -token: line 34, column 58, sym: 57 -line:34 col:62 --FileReader--IDENTIFIER-- -token: line 34, column 62, sym: 98, value: 'FileReader' -line:34 col:72 --(--LPAREN-- -token: line 34, column 72, sym: 19 -line:34 col:73 --argv--IDENTIFIER-- -token: line 34, column 73, sym: 98, value: 'argv' -line:34 col:77 --[--LBRACK-- -token: line 34, column 77, sym: 10 -line:34 col:78 --i--IDENTIFIER-- -token: line 34, column 78, sym: 98, value: 'i' -line:34 col:79 --]--RBRACK-- -token: line 34, column 79, sym: 11 -line:34 col:80 --)--RPAREN-- -token: line 34, column 80, sym: 20 -line:34 col:81 --)--RPAREN-- -token: line 34, column 81, sym: 20 -line:34 col:82 --)--RPAREN-- -token: line 34, column 82, sym: 20 -line:34 col:83 --;--SEMICOLON-- -token: line 34, column 83, sym: 13 -line:36 col:9 --Symbol--IDENTIFIER-- -token: line 36, column 9, sym: 98, value: 'Symbol' -line:36 col:16 --s--IDENTIFIER-- -token: line 36, column 16, sym: 98, value: 's' -line:36 col:17 --;--SEMICOLON-- -token: line 36, column 17, sym: 13 -line:37 col:9 --do--DO-- -token: line 37, column 9, sym: 47 -line:37 col:12 --{--LBRACE-- -token: line 37, column 12, sym: 16 -line:38 col:11 --s--IDENTIFIER-- -token: line 38, column 11, sym: 98, value: 's' -line:38 col:13 --=--EQ-- -token: line 38, column 13, sym: 18 -line:38 col:15 --scanner--IDENTIFIER-- -token: line 38, column 15, sym: 98, value: 'scanner' -line:38 col:22 --.--DOT-- -token: line 38, column 22, sym: 12 -line:38 col:23 --debug_next_token--IDENTIFIER-- -token: line 38, column 23, sym: 98, value: 'debug_next_token' -line:38 col:39 --(--LPAREN-- -token: line 38, column 39, sym: 19 -line:38 col:40 --)--RPAREN-- -token: line 38, column 40, sym: 20 -line:38 col:41 --;--SEMICOLON-- -token: line 38, column 41, sym: 13 -line:39 col:11 --System--IDENTIFIER-- -token: line 39, column 11, sym: 98, value: 'System' -line:39 col:17 --.--DOT-- -token: line 39, column 17, sym: 12 -line:39 col:18 --out--IDENTIFIER-- -token: line 39, column 18, sym: 98, value: 'out' -line:39 col:21 --.--DOT-- -token: line 39, column 21, sym: 12 -line:39 col:22 --println--IDENTIFIER-- -token: line 39, column 22, sym: 98, value: 'println' -line:39 col:29 --(--LPAREN-- -token: line 39, column 29, sym: 19 -line:39 col:38 --"--STRING_LITERAL-- -token: line 39, column 38, sym: 97, value: 'token: ' -line:39 col:39 --+--PLUS-- -token: line 39, column 39, sym: 60 -line:39 col:40 --s--IDENTIFIER-- -token: line 39, column 40, sym: 98, value: 's' -line:39 col:41 --)--RPAREN-- -token: line 39, column 41, sym: 20 -line:39 col:42 --;--SEMICOLON-- -token: line 39, column 42, sym: 13 -line:40 col:9 --}--RBRACE-- -token: line 40, column 9, sym: 17 -line:40 col:11 --while--WHILE-- -token: line 40, column 11, sym: 48 -line:40 col:17 --(--LPAREN-- -token: line 40, column 17, sym: 19 -line:40 col:18 --s--IDENTIFIER-- -token: line 40, column 18, sym: 98, value: 's' -line:40 col:19 --.--DOT-- -token: line 40, column 19, sym: 12 -line:40 col:20 --sym--IDENTIFIER-- -token: line 40, column 20, sym: 98, value: 'sym' -line:40 col:24 --!=--NOTEQ-- -token: line 40, column 24, sym: 75 -line:40 col:27 --sym--IDENTIFIER-- -token: line 40, column 27, sym: 98, value: 'sym' -line:40 col:30 --.--DOT-- -token: line 40, column 30, sym: 12 -line:40 col:31 --EOF--IDENTIFIER-- -token: line 40, column 31, sym: 98, value: 'EOF' -line:40 col:34 --)--RPAREN-- -token: line 40, column 34, sym: 20 -line:40 col:35 --;--SEMICOLON-- -token: line 40, column 35, sym: 13 -line:42 col:9 --System--IDENTIFIER-- -token: line 42, column 9, sym: 98, value: 'System' -line:42 col:15 --.--DOT-- -token: line 42, column 15, sym: 12 -line:42 col:16 --out--IDENTIFIER-- -token: line 42, column 16, sym: 98, value: 'out' -line:42 col:19 --.--DOT-- -token: line 42, column 19, sym: 12 -line:42 col:20 --println--IDENTIFIER-- -token: line 42, column 20, sym: 98, value: 'println' -line:42 col:27 --(--LPAREN-- -token: line 42, column 27, sym: 19 -line:42 col:39 --"--STRING_LITERAL-- -token: line 42, column 39, sym: 97, value: 'No errors.' -line:42 col:40 --)--RPAREN-- -token: line 42, column 40, sym: 20 -line:42 col:41 --;--SEMICOLON-- -token: line 42, column 41, sym: 13 -line:43 col:7 --}--RBRACE-- -token: line 43, column 7, sym: 17 -line:44 col:7 --catch--CATCH-- -token: line 44, column 7, sym: 55 -line:44 col:13 --(--LPAREN-- -token: line 44, column 13, sym: 19 -line:44 col:14 --Exception--IDENTIFIER-- -token: line 44, column 14, sym: 98, value: 'Exception' -line:44 col:24 --e--IDENTIFIER-- -token: line 44, column 24, sym: 98, value: 'e' -line:44 col:25 --)--RPAREN-- -token: line 44, column 25, sym: 20 -line:44 col:27 --{--LBRACE-- -token: line 44, column 27, sym: 16 -line:45 col:9 --e--IDENTIFIER-- -token: line 45, column 9, sym: 98, value: 'e' -line:45 col:10 --.--DOT-- -token: line 45, column 10, sym: 12 -line:45 col:11 --printStackTrace--IDENTIFIER-- -token: line 45, column 11, sym: 98, value: 'printStackTrace' -line:45 col:26 --(--LPAREN-- -token: line 45, column 26, sym: 19 -line:45 col:27 --System--IDENTIFIER-- -token: line 45, column 27, sym: 98, value: 'System' -line:45 col:33 --.--DOT-- -token: line 45, column 33, sym: 12 -line:45 col:34 --out--IDENTIFIER-- -token: line 45, column 34, sym: 98, value: 'out' -line:45 col:37 --)--RPAREN-- -token: line 45, column 37, sym: 20 -line:45 col:38 --;--SEMICOLON-- -token: line 45, column 38, sym: 13 -line:46 col:9 --System--IDENTIFIER-- -token: line 46, column 9, sym: 98, value: 'System' -line:46 col:15 --.--DOT-- -token: line 46, column 15, sym: 12 -line:46 col:16 --exit--IDENTIFIER-- -token: line 46, column 16, sym: 98, value: 'exit' -line:46 col:20 --(--LPAREN-- -token: line 46, column 20, sym: 19 -line:46 col:21 --1--INTEGER_LITERAL-- -token: line 46, column 21, sym: 93, value: '1' -line:46 col:22 --)--RPAREN-- -token: line 46, column 22, sym: 20 -line:46 col:23 --;--SEMICOLON-- -token: line 46, column 23, sym: 13 -line:47 col:7 --}--RBRACE-- -token: line 47, column 7, sym: 17 -line:48 col:5 --}--RBRACE-- -token: line 48, column 5, sym: 17 -line:49 col:3 --}--RBRACE-- -token: line 49, column 3, sym: 17 -line:50 col:1 --}--RBRACE-- -token: line 50, column 1, sym: 17 -line:51 col:1 ----EOF-- -token: line 51, column 1, sym: 0 -No errors. diff --git a/jflex/examples/pom.xml b/jflex/examples/pom.xml index 554dc0ebd..626d620b6 100644 --- a/jflex/examples/pom.xml +++ b/jflex/examples/pom.xml @@ -1,16 +1,21 @@ - - 4.0.0 - de.flex.examples - pom - 1 - - Examples on how to use JFlex and jflex-maven-plugin - - pom - - cup-maven - simple-maven - standalone-maven - + + 4.0.0 + de.flex.examples + pom + 1 + + Examples on how to use JFlex and jflex-maven-plugin + + pom + + cup-interpreter + cup-java + cup-lcalc + simple + standalone + zero-reader + diff --git a/jflex/examples/simple-maven/README.md b/jflex/examples/simple-maven/README.md deleted file mode 100644 index 4b1823bcc..000000000 --- a/jflex/examples/simple-maven/README.md +++ /dev/null @@ -1,13 +0,0 @@ -Simple usage of the jflex-maven-plugin -====================================== - -This is a small Maven project that demonstrates the use of `jflex-maven-plugin`. - -## Files - -* `main/flex/simple.flex`: - the example specification -* `test/resources/test.txt`: - sample input -* `test/resources/output.good`: - expected output matching the sample input from `test.txt` diff --git a/jflex/examples/simple-maven/src/main/java/Utility.java b/jflex/examples/simple-maven/src/main/java/Utility.java deleted file mode 100644 index e6e495543..000000000 --- a/jflex/examples/simple-maven/src/main/java/Utility.java +++ /dev/null @@ -1,23 +0,0 @@ -/** - * A small utility class. - * TODO: use resource bundle - */ -class Utility { - - private static final String errorMsg[] = { - "Error: Unmatched end-of-comment punctuation.", - "Error: Unmatched start-of-comment punctuation.", - "Error: Unclosed string.", - "Error: Illegal character." - }; - - public static final int E_ENDCOMMENT = 0; - public static final int E_STARTCOMMENT = 1; - public static final int E_UNCLOSEDSTR = 2; - public static final int E_UNMATCHED = 3; - - public static void error(int code) { - System.err.println(errorMsg[code]); - } -} - diff --git a/jflex/examples/simple-maven/src/main/java/Yytoken.java b/jflex/examples/simple-maven/src/main/java/Yytoken.java deleted file mode 100644 index 64ea3707f..000000000 --- a/jflex/examples/simple-maven/src/main/java/Yytoken.java +++ /dev/null @@ -1,28 +0,0 @@ -/** - * The tokens returned by the scanner. - * - */ -class Yytoken { - public int m_index; - public String m_text; - public int m_line; - public int m_charBegin; - public int m_charEnd; - - Yytoken (int index, String text, int line, int charBegin, int charEnd) { - m_index = index; - m_text = text; - m_line = line; - m_charBegin = charBegin; - m_charEnd = charEnd; - } - - public String toString() { - return "Text : "+m_text+ - "\nindex : "+m_index+ - "\nline : "+m_line+ - "\ncBeg. : "+m_charBegin+ - "\ncEnd. : "+m_charEnd; - } -} - diff --git a/jflex/examples/simple-maven/src/site/apt/usage.apt b/jflex/examples/simple-maven/src/site/apt/usage.apt deleted file mode 100644 index 9f933295e..000000000 --- a/jflex/examples/simple-maven/src/site/apt/usage.apt +++ /dev/null @@ -1,100 +0,0 @@ - ------ - Usage - ------ - Gerwin Klein - Régis Décamps - ------ - November 2, 2007 - ------ - - -About this project - - This sample project parses a grammar with JFlex. - - The project management is done with Maven. - - The project contains a simple grammar for the "Toy programming language". - It is the example from the JLex web site with some small - modifications, to make it a bit more readable. - It does nothing really useful, because there is no parser for - the toy programming language. It's just a demonstration how a - small simple scanner looks like. - - -* JFlex - - JFlex is a parser generator. Given a grammar, JFlex generate - Java (TM) code to parse documents that follow this grammar. - -* Maven - - Maven is a project management framework. - The project is described in as a POM (project object model, - stored into <<>>). - This document isn't intended to be a tutorial on the use of Maven 3, - you should consult the {{{http://maven.apache.org/}Maven web site}}. - - The integration of JFlex and Maven is done with - {{{http://jflex.sourceforge.net/jflex-maven-plugin/}jflex-maven-plugin}}. - - -Usage - -* mvn generate-sources - - The jflex-maven-plugin will read the grammar - <<>> - and generate a Java scanner <<>> - in <<>> - - This is defined by the following section - -+--------- - - - - de.jflex - jflex-maven-plugin - 1.7.0 - - - - generate - - - - - - -+------------ - - By default, the jflex-maven-plugin generates a scanner/parser for every file - in <<>>. - - -* mvn test - - This goal test the generated scanner (if required, the lexer will be - generated and all Java classes will be compiled) - by running all tests in <<>>. - - There is only one test in <<>>. - In this test, - the scanner is run with the input file <<>>. - - By default, the scanner outputs debugging information about each - returned token to <<>> until the end of file is reached, - or an error occurs. - But in the test, the output is redirected into <<>>. - - The test is successful if every line match - with <<>>, - which is the expected scanner debugging information. - - -References - - * {{{http://jflex.sourceforge.net/maven-flex-plugin/}maven-flex-plugin}} - - * {{{http://maven.apache.org/}Maven web site}}. diff --git a/jflex/examples/simple-maven/src/site/site.xml b/jflex/examples/simple-maven/src/site/site.xml deleted file mode 100644 index 78b4fbe53..000000000 --- a/jflex/examples/simple-maven/src/site/site.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/jflex/examples/simple-maven/src/test/java/YylexTest.java b/jflex/examples/simple-maven/src/test/java/YylexTest.java deleted file mode 100644 index 635697874..000000000 --- a/jflex/examples/simple-maven/src/test/java/YylexTest.java +++ /dev/null @@ -1,59 +0,0 @@ -import java.io.BufferedReader; -import java.io.File; -import java.io.FileOutputStream; -import java.io.FileReader; -import java.io.IOException; -import java.io.PrintStream; - -import junit.framework.TestCase; - -/** - * This is an integration test. - * - * The class Yylex is generated by JFLex from - * src/main/jflex/simple.flex. - * - * @author JFlex contributors. - * - */ -public class YylexTest extends TestCase { - - private static final String OUTPUT_FILE = "target/output.actual"; - - /** - * Test that Yylex parser behaves like expected. - * - * @throws IOException - */ - public void testOutput() throws IOException { - String[] argv = new String[1]; - argv[0] = "src/test/resources/test.txt"; - - // the Yylex prints status on stdout - File actual = new File("target/output.actual"); - actual.delete(); - FileOutputStream fos = new FileOutputStream(OUTPUT_FILE, true); - System.setOut(new PrintStream(fos)); - - Yylex.main(argv); - - fos.close(); - - // test actual is expected - File expected = new File("src/test/resources/output.good"); - assertTrue(expected.isFile()); - assertTrue(actual.isFile()); - - BufferedReader actualContent = new BufferedReader( - new FileReader(actual)); - BufferedReader expectedContent = new BufferedReader(new FileReader( - expected)); - - for (int lineNumber = 1;lineNumber!=-1; lineNumber++) { - String expectedLine = expectedContent.readLine(); - String actualLine = actualContent.readLine(); - assertEquals("Line "+lineNumber, expectedLine, actualLine); - if (expectedLine==null) lineNumber=-2; //EOF - } - } -} diff --git a/jflex/examples/simple/BUILD b/jflex/examples/simple/BUILD new file mode 100644 index 000000000..83f0d765b --- /dev/null +++ b/jflex/examples/simple/BUILD @@ -0,0 +1,30 @@ +package(default_visibility = ["//visibility:public"]) + +load("@jflex_rules//jflex:jflex.bzl", "jflex") + +# The best practice is to define the rules in their respective directory in +# - src/main/java/org/example/foo/BUILD +# - src/main/jflex/BUILD +# - src/test/java/org/example/foo/BUILD +# - etc. +# However, this example is simple enough and we can define all rules here. + +java_binary( + name = "simple_bin", + main_class = "Yylex", + runtime_deps = [":simple"], +) + +java_library( + name = "simple", + # glob is not a best practice, but it's good enough for this example + srcs = glob(["src/main/java/**/*.java"]) + [":gen_lexer"], + deps = ["//cup:cup_runtime"], +) + +jflex( + name = "gen_lexer", + srcs = ["src/main/jflex/simple.flex"], + jflex_bin = "//jflex:jflex_bin", + outputs = ["Yylex.java"], +) diff --git a/jflex/examples/simple/README.md b/jflex/examples/simple/README.md new file mode 100644 index 000000000..eb748102e --- /dev/null +++ b/jflex/examples/simple/README.md @@ -0,0 +1,139 @@ +# Simple usage + +This is a very simple example of how to generate a lexer with JFlex: + +- for a very simple grammar of the _Toy programmimg language_ + described in the user manual. +- without integration to a parser. As a result, the program + does nothing really useful, because there is no parser. + +The generated lexer has the default name **Yylex** because the flex +specification doesn't define a name with the `%class`. + +The project comes with a test class for the lexer: `YylexTest`. +The test: +- runs the lexer in debug mode on `test.txt` +- collects the output of JFlex by redirecting `System.out` +- and verifies that the verbose logs of JFlex corresponds to + the expected content of `output.good`. + +## Files + +* `main/flex/simple.flex`: + the simple grammar specification +* `test/resources/test.txt`: + sample input +* `test/resources/output.good`: + _golden file_, i.e. expected output corresponding to the sample input from `test.txt` +* `tests/java/YylexTest.java`: + jUnit integration test that running the lexer on the sample input produces + the same output as the _golden file_. + +## Compile and test + +### Using Maven + +**Tip** Jflex comes with the Maven wrapper. +You can use `../../mvnw` instead of `mvn` if the later is not installed on your +system. + +#### Generate the lexer + + mvn generate-sources + +The **jflex-maven-plugin** will read the grammar `src/main/jflex/simple.jflex` +and generate a Java scanner **Yylex** in +`target/generated-sources/flex/Yylex.java`. + +This is defined by the following section +```xml + + + + de.jflex + jflex-maven-plugin + 1.7.0 + + + + generate + + + + + + +``` + +By default, the **jflex-maven-plugin** generates a lexer (scanner) for every file +in `src/main/jflex/`. + +#### Build + + mvn compile + +The compile phase will build all Java classes, including those generated automatically. + +You will find the class files in `target/classes`. + +**Tip** In fact, you don't have to invoke `mvn generate-sources` explicitly, +the **compile** phase will do it automatically. + +#### Test + + mvn test + +The **test** phase executes the test in `src/test/java`. + + This goal test the generated scanner (if required, the lexer will be + generated and all Java classes will be compiled) + by running all tests in <<>>. + + There is only one test in <<>>. + In this test, + the scanner is run with the input file <<>>. + + By default, the scanner outputs debugging information about each + returned token to <<>> until the end of file is reached, + or an error occurs. + But in the test, the output is redirected into <<>>. + + The test is successful if every line match + with <<>>, + which is the expected scanner debugging information. + + + ../../mvnw package + +Expected output: +* `target/generated-sources/jflex/Yylex.java` Java code generated from the flex file. + +**Rem** Notice that Maven ran a test for you. + +### Build with Bazel + +#### Build + + blaze build //simple + +Expected output: +* (`examples/`)`bazel-genfiles/simple/Yylex.java` Java code generated from the flex file. + +#### Test + +To execute the tests + + blaze test //simple/... + +To run the lexer on any file + + bazel run //simple:simple_bin -- /full/path/to/src/test/resources/test.txt + +**N.B.** Relative path doesn't work in `bazel run`. + +**Rem:** The Bazel commands work from any directory in the workspace. + +Alternatively, use the generated artifact. From the `examples` directory: + + bazel-bin/simple/simple_bin simple/src/test/resources/test.txt + \ No newline at end of file diff --git a/jflex/examples/simple/build.xml b/jflex/examples/simple/build.xml new file mode 100644 index 000000000..3a68e9344 --- /dev/null +++ b/jflex/examples/simple/build.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/jflex/examples/simple-maven/pom.xml b/jflex/examples/simple/pom.xml similarity index 72% rename from jflex/examples/simple-maven/pom.xml rename to jflex/examples/simple/pom.xml index f1d0a17db..7d160afa2 100644 --- a/jflex/examples/simple-maven/pom.xml +++ b/jflex/examples/simple/pom.xml @@ -2,7 +2,7 @@ 4.0.0 de.flex.examples - simple-maven + simple 1.0 A scanner for a toy programming language. It is the example from the JLex website with some small @@ -15,12 +15,27 @@ junit junit - 3.8.2 + 4.12 + test + + + com.google.truth + truth + 0.36 test + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.0 + + 1.6 + 1.6 + + de.jflex jflex-maven-plugin diff --git a/jflex/examples/simple/src/main/java/Utility.java b/jflex/examples/simple/src/main/java/Utility.java new file mode 100644 index 000000000..d770230b4 --- /dev/null +++ b/jflex/examples/simple/src/main/java/Utility.java @@ -0,0 +1,20 @@ +/** A small utility class. */ +// TODO: use resource bundle +class Utility { + + private static final String errorMsg[] = { + "Error: Unmatched end-of-comment punctuation.", + "Error: Unmatched start-of-comment punctuation.", + "Error: Unclosed string.", + "Error: Illegal character." + }; + + public static final int E_ENDCOMMENT = 0; + public static final int E_STARTCOMMENT = 1; + public static final int E_UNCLOSEDSTR = 2; + public static final int E_UNMATCHED = 3; + + public static void error(int code) { + System.err.println(errorMsg[code]); + } +} diff --git a/jflex/examples/simple/src/main/java/Yytoken.java b/jflex/examples/simple/src/main/java/Yytoken.java new file mode 100644 index 000000000..568ef732a --- /dev/null +++ b/jflex/examples/simple/src/main/java/Yytoken.java @@ -0,0 +1,29 @@ +/** The tokens returned by the scanner. */ +class Yytoken { + public int m_index; + public String m_text; + public int m_line; + public int m_charBegin; + public int m_charEnd; + + Yytoken(int index, String text, int line, int charBegin, int charEnd) { + m_index = index; + m_text = text; + m_line = line; + m_charBegin = charBegin; + m_charEnd = charEnd; + } + + public String toString() { + return "Text : " + + m_text + + "\nindex : " + + m_index + + "\nline : " + + m_line + + "\ncBeg. : " + + m_charBegin + + "\ncEnd. : " + + m_charEnd; + } +} diff --git a/jflex/examples/simple-maven/src/main/jflex/simple.flex b/jflex/examples/simple/src/main/jflex/simple.flex similarity index 100% rename from jflex/examples/simple-maven/src/main/jflex/simple.flex rename to jflex/examples/simple/src/main/jflex/simple.flex diff --git a/jflex/examples/simple/src/test/BUILD b/jflex/examples/simple/src/test/BUILD new file mode 100644 index 000000000..16e157848 --- /dev/null +++ b/jflex/examples/simple/src/test/BUILD @@ -0,0 +1,12 @@ +java_test( + name = "YylexTest", + srcs = ["java/YylexTest.java"], + data = [ + "data/output.good", + "data/test.txt", + ], + deps = [ + "//jflex/examples/simple", + "//third_party/com/google/truth", + ], +) diff --git a/jflex/examples/simple-maven/src/test/resources/output.good b/jflex/examples/simple/src/test/data/output.good similarity index 100% rename from jflex/examples/simple-maven/src/test/resources/output.good rename to jflex/examples/simple/src/test/data/output.good diff --git a/jflex/examples/simple-maven/src/test/resources/test.txt b/jflex/examples/simple/src/test/data/test.txt similarity index 100% rename from jflex/examples/simple-maven/src/test/resources/test.txt rename to jflex/examples/simple/src/test/data/test.txt diff --git a/jflex/examples/simple/src/test/java/YylexTest.java b/jflex/examples/simple/src/test/java/YylexTest.java new file mode 100644 index 000000000..420097521 --- /dev/null +++ b/jflex/examples/simple/src/test/java/YylexTest.java @@ -0,0 +1,95 @@ +import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth.assertWithMessage; + +import com.google.common.base.Objects; +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.PrintStream; +import junit.framework.TestCase; + +/** + * This is an integration test. + * + *

The class Yylex is generated by JFLex from {@code src/main/jflex/simple.flex}. + * + * @author Régis Décamps + */ +public class YylexTest extends TestCase { + + private ByteArrayOutputStream outputStream; + + @Override + public void setUp() { + // the Yylex prints status on stdout. Redirect to ByteOutputStream. + outputStream = new ByteArrayOutputStream(); + System.setOut(new PrintStream(outputStream)); + } + + @Override + public void tearDown() throws Exception { + outputStream.close(); + } + + /** Tests that the generated {@link Yylex} lexer behaves like expected. */ + public void testOutput() throws Exception { + File inputFile = openFile("src/test/data/test.txt"); + assertThat(inputFile.isFile()).isTrue(); + + String[] argv = new String[] {inputFile.getPath()}; + + Yylex.main(argv); + + // test actual is expected + File expected = openFile("src/test/data/output.good"); + assertThat(expected.isFile()).isTrue(); + + BufferedReader actualContent = readOutputStream(); + BufferedReader expectedContent = new BufferedReader(new FileReader(expected)); + + for (int lineNumber = 1; lineNumber != -1; lineNumber++) { + String expectedLine = expectedContent.readLine(); + String actualLine = actualContent.readLine(); + assertWithMessage("Line " + lineNumber).that(actualLine).isEqualTo(expectedLine); + if (expectedLine == null) lineNumber = -2; // EOF + } + } + + private BufferedReader readOutputStream() { + byte[] rawOutput = outputStream.toByteArray(); + return new BufferedReader(new InputStreamReader(new ByteArrayInputStream(rawOutput))); + } + + /** + * Opens the given file. + * + *

This method also works around a build difficulty: + * + *

    + *
  • Maven uses the directory that contains {@code pom.xml} as a working directory, i.e. + * {@code examples/simple} + *
  • ant uses the directory that contains {@code build.xml} as a working directory, i.e. + * {@code examples/simple} + *
  • bazel uses the directory that contains {@code WORKSPACE} as a working directory, i.e. + * {@code __main__} in runfiles. + *
+ */ + private File openFile(String pathName) throws IOException { + String path = pathName; + File pwd = new File(".").getCanonicalFile(); + assertThat(pwd.isDirectory()).isTrue(); + if (Objects.equal(pwd.getName(), "__main__")) { + path = "jflex/examples/simple/" + path; + } + File file = new File(path); + if (!file.isFile()) { + throw new FileNotFoundException(path); + } + return file; + } +} diff --git a/jflex/examples/standalone-maven/src/test/java/de/jflex/example/standalone/SubstTest.java b/jflex/examples/standalone-maven/src/test/java/de/jflex/example/standalone/SubstTest.java deleted file mode 100644 index f51675b55..000000000 --- a/jflex/examples/standalone-maven/src/test/java/de/jflex/example/standalone/SubstTest.java +++ /dev/null @@ -1,46 +0,0 @@ -package de.jflex.example.standalone; - -import java.io.BufferedReader; -import java.io.File; -import java.io.FileOutputStream; -import java.io.FileReader; -import java.io.IOException; -import java.io.PrintStream; -import java.io.Reader; -import junit.framework.TestCase; - - - -public class SubstTest extends TestCase { - private static final String OUTPUT_FILE = "target/sample.out"; - - public void testSample() throws IOException { - // the standalon Subst prints status on stdout - // redirecte it into a file - String[] argv = new String[1]; - argv[0] = "src/test/resources/sample.in"; - File actual = new File(OUTPUT_FILE); - actual.delete(); - FileOutputStream fos = new FileOutputStream(OUTPUT_FILE, true); - System.setOut(new PrintStream(fos)); - - Subst.main(argv); - - fos.close(); - - BufferedReader actualContent = new BufferedReader(new FileReader( - actual)); - - // the expected result is in a file - Reader expected = new FileReader("src/test/resources/sample.expected"); - BufferedReader expectedContent = new BufferedReader(expected); - - String expectedLine, actualLine; - do { - expectedLine = expectedContent.readLine(); - actualLine = actualContent.readLine(); - - assertEquals(expectedLine, actualLine); - } while (expectedLine != null); - } -} diff --git a/jflex/examples/standalone-maven/pom.xml b/jflex/examples/standalone/pom.xml similarity index 74% rename from jflex/examples/standalone-maven/pom.xml rename to jflex/examples/standalone/pom.xml index d6bdedd87..5d03314a7 100644 --- a/jflex/examples/standalone-maven/pom.xml +++ b/jflex/examples/standalone/pom.xml @@ -15,9 +15,24 @@ of "hello" by "hello <name> !".
3.8.2 test + + com.google.truth + truth + 0.36 + test + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.0 + + 1.6 + 1.6 + + de.jflex jflex-maven-plugin diff --git a/jflex/examples/standalone-maven/src/main/jflex/standalone.flex b/jflex/examples/standalone/src/main/jflex/standalone.flex similarity index 100% rename from jflex/examples/standalone-maven/src/main/jflex/standalone.flex rename to jflex/examples/standalone/src/main/jflex/standalone.flex diff --git a/jflex/examples/standalone/src/test/java/de/jflex/example/standalone/SubstTest.java b/jflex/examples/standalone/src/test/java/de/jflex/example/standalone/SubstTest.java new file mode 100644 index 000000000..a2895e97a --- /dev/null +++ b/jflex/examples/standalone/src/test/java/de/jflex/example/standalone/SubstTest.java @@ -0,0 +1,45 @@ +package de.jflex.example.standalone; + +import static com.google.common.truth.Truth.assertThat; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.IOException; +import java.io.PrintStream; +import java.io.Reader; +import junit.framework.TestCase; + +public class SubstTest extends TestCase { + private static final String OUTPUT_FILE = "target/sample.out"; + + public void testSample() throws IOException { + // the standalon Subst prints status on stdout + // redirecte it into a file + String[] argv = new String[1]; + argv[0] = "src/test/resources/sample.in"; + File actual = new File(OUTPUT_FILE); + actual.delete(); + FileOutputStream fos = new FileOutputStream(OUTPUT_FILE, true); + System.setOut(new PrintStream(fos)); + + Subst.main(argv); + + fos.close(); + + BufferedReader actualContent = new BufferedReader(new FileReader(actual)); + + // the expected result is in a file + Reader expected = new FileReader("src/test/resources/sample.expected"); + BufferedReader expectedContent = new BufferedReader(expected); + + String expectedLine, actualLine; + do { + expectedLine = expectedContent.readLine(); + actualLine = actualContent.readLine(); + + assertThat(actualLine).isEqualTo(expectedLine); + } while (expectedLine != null); + } +} diff --git a/jflex/examples/standalone-maven/src/test/resources/sample.expected b/jflex/examples/standalone/src/test/resources/sample.expected similarity index 100% rename from jflex/examples/standalone-maven/src/test/resources/sample.expected rename to jflex/examples/standalone/src/test/resources/sample.expected diff --git a/jflex/examples/standalone-maven/src/test/resources/sample.in b/jflex/examples/standalone/src/test/resources/sample.in similarity index 100% rename from jflex/examples/standalone-maven/src/test/resources/sample.in rename to jflex/examples/standalone/src/test/resources/sample.in diff --git a/jflex/examples/zero-reader/Makefile b/jflex/examples/zero-reader/Makefile index 1fafe4775..024b707e1 100644 --- a/jflex/examples/zero-reader/Makefile +++ b/jflex/examples/zero-reader/Makefile @@ -1,30 +1,26 @@ JFLEX = ../../bin/jflex -JAVA = java -JAVAC = javac + LEXER = ZeroLexer # -------------------------------------------------- -all: test +all: clean compile -test: output.txt - @(diff output.txt lexer-output.good && echo "Test OK!") || echo "Test failed!" +test: out/output.txt + @(diff --strip-trailing-cr src/test/data/lexer-output.good $< && echo "Test OK") || (echo "Test FAILED"; exit 1) -output.txt: $(LEXER).class test-input.txt - $(JAVA) $(LEXER) test-input.txt > output.txt +out/output.txt: out/$(LEXER).class src/test/data/test-input.txt + java -cp out $(LEXER) src/test/data/test-input.txt > $@ -compile: $(LEXER).class +compile: out/$(LEXER).class -$(LEXER).class: $(LEXER).java FunkyReader.java ZeroReader.java - $(JAVAC) $^ +out/$(LEXER).class: out/$(LEXER).java src/main/java/*.java + javac -d out $^ -$(LEXER).java: zero-lexer.jflex - $(JFLEX) $< +out/$(LEXER).java: src/main/jflex/zero-lexer.jflex + $(JFLEX) -d out $< clean: - rm -f *.class - rm -f *~ - rm -f $(LEXER).java - rm -f output.txt + rm -rf out diff --git a/jflex/examples/zero-reader/README.md b/jflex/examples/zero-reader/README.md index 571d2ef61..a1cba619f 100644 --- a/jflex/examples/zero-reader/README.md +++ b/jflex/examples/zero-reader/README.md @@ -1,3 +1,5 @@ +# Readers returning 0 characters + This example shows what to do about input Readers that sometimes return 0 characters. @@ -53,3 +55,23 @@ blocking if no input is available yet (if `read()` returns 0, it means the character 0 has been read, not that 0 characters have been read). This method is usually too inefficient to use always, but 0-character returns tend to be rare, so the impact of using it only in those cases is small. + +Build +----- + +### Build with maven + + ../../mvnw package + java -jar target/zero-reader-1.0.jar + +e.g. + + java -jar target/zero-reader-1.0.jar src/test/data/test-input.txt + +and expect `src/test/data/lexer-output.good`. + +### Build with make + + make + make test + java -cp out ZeroLexer diff --git a/jflex/examples/zero-reader/pom.xml b/jflex/examples/zero-reader/pom.xml new file mode 100644 index 000000000..02aae689e --- /dev/null +++ b/jflex/examples/zero-reader/pom.xml @@ -0,0 +1,66 @@ + + + 4.0.0 + de.flex.examples + zero-reader + 1.0 + Example of a Reader wrapper that supports reading 0 character + This example shows what to do about input Readers that sometimes return 0 + characters. + + + de.jflex + cup_runtime + 11b + + + junit + junit + 3.8.2 + test + + + + + + de.jflex + jflex-maven-plugin + 1.7.1-SNAPSHOT + + + + generate + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.0 + + 1.6 + 1.6 + + + + org.apache.maven.plugins + maven-jar-plugin + 3.1.0 + + + + true + ZeroLexer + + + + + + + + UTF-8 + + diff --git a/jflex/examples/zero-reader/FunkyReader.java b/jflex/examples/zero-reader/src/main/java/FunkyReader.java similarity index 85% rename from jflex/examples/zero-reader/FunkyReader.java rename to jflex/examples/zero-reader/src/main/java/FunkyReader.java index 9f4d7f645..db04aa9bc 100644 --- a/jflex/examples/zero-reader/FunkyReader.java +++ b/jflex/examples/zero-reader/src/main/java/FunkyReader.java @@ -8,16 +8,14 @@ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -import java.io.Reader; import java.io.IOException; - +import java.io.Reader; /** * Reader that returns 0 chars read every once in a while. - * - * This is a demonstration of a problematic Reader that does not - * implement the Reader specification correctly. Do not use. + * + *

This is a demonstration of a problematic Reader that does not implement the Reader + * specification correctly. Do not use. */ public class FunkyReader extends Reader { @@ -31,9 +29,8 @@ public FunkyReader(Reader r) { public int read(char[] cbuf, int off, int len) throws IOException { if (!do_zero) { do_zero = true; - return reader.read(cbuf,off,Math.min(10,len)); - } - else { + return reader.read(cbuf, off, Math.min(10, len)); + } else { do_zero = false; return 0; } @@ -42,5 +39,4 @@ public int read(char[] cbuf, int off, int len) throws IOException { public void close() throws IOException { reader.close(); } - } diff --git a/jflex/examples/zero-reader/ZeroReader.java b/jflex/examples/zero-reader/src/main/java/ZeroReader.java similarity index 61% rename from jflex/examples/zero-reader/ZeroReader.java rename to jflex/examples/zero-reader/src/main/java/ZeroReader.java index b2436d614..2e2f359f0 100644 --- a/jflex/examples/zero-reader/ZeroReader.java +++ b/jflex/examples/zero-reader/src/main/java/ZeroReader.java @@ -8,68 +8,59 @@ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ -import java.io.Reader; import java.io.IOException; - +import java.io.Reader; /** - * A Reader wrapper that guards against Readers that sometimes - * return 0 characters instead of blocking. + * A Reader wrapper that guards against Readers that sometimes return 0 characters instead of + * blocking. * - * If 0 character returns are rare, efficiency loss will be minimal. + *

If 0 character returns are rare, efficiency loss will be minimal. */ public class ZeroReader extends Reader { - /** - * The underlying Reader that is being wrapped. - */ + /** The underlying Reader that is being wrapped. */ private Reader reader; - /** - * A new ZeroReader wrapper for a Reader that does not always - * block appropriately in read(char[],int,int). + * A new ZeroReader wrapper for a Reader that does not always block appropriately in + * read(char[],int,int). * - * @param reader the original Reader to wrap + * @param reader the original Reader to wrap */ public ZeroReader(Reader reader) { this.reader = reader; } - /** - * Read len characters from the underlying Reader into the buffer - * cbuf at position off. Blocks until input is available. - * - * Relies on the method char read() of the underlying Reader to block - * until at least one character is available. - * - * @param buf the buffer to write into - * @param off the offset from where to write - * @param len the maximum number of characters to write + * Read len characters from the underlying Reader into the buffer cbuf at position off. Blocks + * until input is available. * - * @return -1 for end of stream, number of characters read otherwise. - * Returns 0 if and only if len is less than or equal to 0. + *

Relies on the method char read() of the underlying Reader to block until at least one + * character is available. * - * @throws IOException if the underlying reader throws one or if the - * offset is outside the provided buffer. + * @param buf the buffer to write into + * @param off the offset from where to write + * @param len the maximum number of characters to write + * @return -1 for end of stream, number of characters read otherwise. Returns 0 if and only if len + * is less than or equal to 0. + * @throws IOException if the underlying reader throws one or if the offset is outside the + * provided buffer. */ public int read(char[] cbuf, int off, int len) throws IOException { - int n = reader.read(cbuf,off,len); + int n = reader.read(cbuf, off, len); if (n != 0 || len <= 0) { return n; - } - else { + } else { if (off < cbuf.length) { // Returns the character read, as an integer in the range 0 to 65535 // (0x00-0xffff), or -1 if the end of the stream has been reached int c = reader.read(); - if (c == -1) return -1; + if (c == -1) return -1; cbuf[off] = (char) c; return 1; - } - else throw new IOException("Offset outside buffer"); + } else throw new IOException("Offset outside buffer"); } } @@ -81,5 +72,4 @@ public int read(char[] cbuf, int off, int len) throws IOException { public void close() throws IOException { reader.close(); } - } diff --git a/jflex/examples/zero-reader/zero-lexer.jflex b/jflex/examples/zero-reader/src/main/jflex/zero-lexer.jflex similarity index 100% rename from jflex/examples/zero-reader/zero-lexer.jflex rename to jflex/examples/zero-reader/src/main/jflex/zero-lexer.jflex diff --git a/jflex/examples/zero-reader/lexer-output.good b/jflex/examples/zero-reader/src/test/data/lexer-output.good similarity index 100% rename from jflex/examples/zero-reader/lexer-output.good rename to jflex/examples/zero-reader/src/test/data/lexer-output.good diff --git a/jflex/examples/zero-reader/test-input.txt b/jflex/examples/zero-reader/src/test/data/test-input.txt similarity index 100% rename from jflex/examples/zero-reader/test-input.txt rename to jflex/examples/zero-reader/src/test/data/test-input.txt diff --git a/jflex/pom.xml b/jflex/pom.xml index 85bcc88d8..5236bad1a 100644 --- a/jflex/pom.xml +++ b/jflex/pom.xml @@ -158,6 +158,25 @@ + + org.jacoco + jacoco-maven-plugin + + + + prepare-agent + + + + + jflex/unicode/data/*.java + + + + + diff --git a/jflex/src/main/java/jflex/Action.java b/jflex/src/main/java/jflex/Action.java index 6b3fc1e85..2d7d5ebd5 100644 --- a/jflex/src/main/java/jflex/Action.java +++ b/jflex/src/main/java/jflex/Action.java @@ -9,6 +9,8 @@ package jflex; +import java.util.Objects; + /** * Encapsulates an action in the specification. * @@ -41,8 +43,7 @@ public final class Action { int priority; /** - * Which kind of action this is. (normal, a/b with fixed length a, fixed length b, - * etc) + * Which kind of action this is. (normal, {@code a/b} with fixed length a, fixed length b, etc) */ private int kind = NORMAL; @@ -110,14 +111,14 @@ public String toString() { } /** - * Returns true iff the parameter is an Action with the same content as this one. + * Returns {@code true} iff the parameter is an Action with the same content as this one. * * @param a the object to compare this Action with * @return true if the action strings are equal */ public boolean isEquiv(Action a) { return this == a - || (this.content.equals(a.content) + || (Objects.equals(this.content, a.content) && this.kind == a.kind && this.len == a.len && this.entryState == a.entryState); diff --git a/jflex/src/main/java/jflex/CharClassInterval.java b/jflex/src/main/java/jflex/CharClassInterval.java index 56581599a..2157da948 100644 --- a/jflex/src/main/java/jflex/CharClassInterval.java +++ b/jflex/src/main/java/jflex/CharClassInterval.java @@ -13,7 +13,7 @@ * Stores an interval of characters together with the character class * *

A character belongs to an interval, if its Unicode value is greater than or equal to the - * Unicode value of start and smaller than or equal to the Unicode value of end + * Unicode value of {@code start} and smaller than or equal to the Unicode value of end * . * *

All characters of the interval must belong to the same character class. @@ -33,8 +33,8 @@ public class CharClassInterval { int charClass; /** - * Creates a new CharClassInterval from start to end that belongs to - * character class charClass. + * Creates a new CharClassInterval from {@code start to end} that belongs to + * character class {@code charClass}. * * @param start The first character of the interval * @param end The last character of the interval diff --git a/jflex/src/main/java/jflex/CharClasses.java b/jflex/src/main/java/jflex/CharClasses.java index dd4bf62f4..971b7eb0e 100644 --- a/jflex/src/main/java/jflex/CharClasses.java +++ b/jflex/src/main/java/jflex/CharClasses.java @@ -11,6 +11,7 @@ import java.util.ArrayList; import java.util.List; +import java.util.Objects; /** * Character Classes. @@ -111,8 +112,8 @@ public int getNumClasses() { * Updates the current partition, so that the specified set of characters gets a new character * class. * - *

Characters that are elements of set are not in the same equivalence class with - * characters that are not elements of set. + *

Characters that are elements of {@code set} are not in the same equivalence class with + * characters that are not elements of {@code set}. * * @param set the set of characters to distinguish from the rest * @param caseless if true upper/lower/title case are considered equivalent @@ -129,15 +130,15 @@ public void makeClass(IntCharSet set, boolean caseless) { for (int i = 0; i < oldSize; i++) { IntCharSet x = classes.get(i); - if (x.equals(set)) return; + if (Objects.equals(x, set)) return; IntCharSet and = x.and(set); if (and.containsElements()) { - if (x.equals(and)) { + if (Objects.equals(x, and)) { set.sub(and); continue; - } else if (set.equals(and)) { + } else if (Objects.equals(set, and)) { x.sub(and); classes.add(and); if (DEBUG) { @@ -213,7 +214,7 @@ public String toString() { } /** - * Creates a new character class for the single character singleChar. + * Creates a new character class for the single character {@code singleChar}. * * @param caseless if true upper/lower/title case are considered equivalent * @param singleChar character. @@ -240,8 +241,8 @@ public void makeClass(String str, boolean caseless) { * Updates the current partition, so that the specified set of characters gets a new character * class. * - *

Characters that are elements of the set l are not in the same equivalence class - * with characters that are not elements of the set l. + *

Characters that are elements of the set {@code l} are not in the same equivalence class with + * characters that are not elements of the set {@code l}. * * @param l a List of Interval objects. This List represents a set of characters. The set of * characters is the union of all intervals in the List. @@ -255,10 +256,10 @@ public void makeClass(List l, boolean caseless) { * Updates the current partition, so that the set of all characters not contained in the specified * set of characters gets a new character class. * - *

Characters that are elements of the set v are not in the same equivalence class - * with characters that are not elements of the set v. + *

Characters that are elements of the set {@code v} are not in the same equivalence class with + * characters that are not elements of the set {@code v}. * - *

This method is equivalent to makeClass(v) + *

This method is equivalent to {@code makeClass(v)} * * @param l a List of Interval objects. This List represents a set of characters. The set of * characters is the union of all intervals in the List. @@ -359,8 +360,8 @@ public void check() { /** * Returns an array of all CharClassIntervals in this char class collection. * - *

The array is ordered by char code, i.e. result[i+1].start = result[i].end+1 - * Each CharClassInterval contains the number of the char class it belongs to. + *

The array is ordered by char code, i.e. {@code result[i+1].start = result[i].end+1} Each + * CharClassInterval contains the number of the char class it belongs to. * * @return an array of all {@link jflex.CharClassInterval} in this char class collection. */ diff --git a/jflex/src/main/java/jflex/CountEmitter.java b/jflex/src/main/java/jflex/CountEmitter.java index d80d8f1c6..53a8dfe3f 100644 --- a/jflex/src/main/java/jflex/CountEmitter.java +++ b/jflex/src/main/java/jflex/CountEmitter.java @@ -84,8 +84,8 @@ public void emitUnpack() { * *

Use to move value interval from [0, 0xFFFF] to something different. * - * @param i amount the value will be translated by. Example: i = 1 allows values in - * [-1, 0xFFFE]. + * @param i amount the value will be translated by. Example: {@code i = 1} allows values in [-1, + * 0xFFFE]. */ public void setValTranslation(int i) { this.translate = i; @@ -94,7 +94,7 @@ public void setValTranslation(int i) { /** * Emit one count/value pair. * - *

Automatically translates value by the translate value. + *

Automatically translates value by the {@code translate} value. * * @param count a int. * @param value a int. diff --git a/jflex/src/main/java/jflex/DFA.java b/jflex/src/main/java/jflex/DFA.java index cfefea79f..d85b83bee 100644 --- a/jflex/src/main/java/jflex/DFA.java +++ b/jflex/src/main/java/jflex/DFA.java @@ -15,6 +15,7 @@ import java.io.PrintWriter; import java.util.HashMap; import java.util.Map; +import java.util.Objects; /** * Deterministic finite automata representation in JFlex. Contains minimization algorithm. @@ -31,18 +32,18 @@ public final class DFA { public static final int NO_TARGET = -1; /** - * table[current_state][character] is the next state for current_state with input - * character, NO_TARGET if there is no transition for this input in - * current_state + * table[current_state][character] is the next state for {@code current_state} with input {@code + * character, NO_TARGET} if there is no transition for this input in {@code + * current_state} */ int[][] table; - /** isFinal[state] == true <=> the state state is a final state. */ + /** {@code isFinal[state] == true <=> the state state} is a final state. */ boolean[] isFinal; /** - * action[state] is the action that is to be carried out in state state, - * null if there is no action. + * {@code action[state] is the action that is to be carried out in state state}, + * {@code null} if there is no action. */ Action[] action; @@ -173,7 +174,7 @@ public String toString() { if (isFinal[i]) { result.append("[FINAL"); String l = action[i].lookString(); - if (!l.equals("")) { + if (!Objects.equals(l, "")) { result.append(", "); result.append(l); } @@ -255,7 +256,7 @@ public void checkActions(LexScan scanner, LexParse parser) { EOFActions eofActions = parser.getEOFActions(); for (Action a : scanner.actions) - if (!a.equals(usedActions.get(a)) && !eofActions.isEOFAction(a)) + if (!Objects.equals(a, usedActions.get(a)) && !eofActions.isEOFAction(a)) Out.warning(scanner.file, ErrorMessages.NEVER_MATCH, a.priority - 1, -1); } diff --git a/jflex/src/main/java/jflex/Emitter.java b/jflex/src/main/java/jflex/Emitter.java index abc9c2e07..de45e63d9 100644 --- a/jflex/src/main/java/jflex/Emitter.java +++ b/jflex/src/main/java/jflex/Emitter.java @@ -19,6 +19,7 @@ import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; +import jflex.io.FileUtil; /** * This class manages the actual code generation, putting the scanner together, filling in skeleton @@ -46,6 +47,7 @@ public final class Emitter { private static final int NOLOOK = 8; private File inputFile; + final String outputFileName; private PrintWriter out; private Skeleton skel; @@ -85,6 +87,7 @@ public Emitter(File inputFile, LexParse parser, DFA dfa) throws IOException { String name = getBaseName(parser.scanner.className) + ".java"; File outputFile = normalize(name, inputFile); + outputFileName = outputFile.getAbsolutePath(); Out.println("Writing code to \"" + outputFile + "\""); @@ -406,7 +409,15 @@ private void emitNextInput() { } private void emitHeader() { - println("/* The following code was generated by JFlex " + Main.version + " */"); + println("// DO NOT EDIT"); + println("// Generated by JFlex " + LexGenerator.VERSION + " http://jflex.de/"); + String path = FileUtil.getRelativePath(Options.getRootDirectory(), inputFile); + if (File.separatorChar == '\\') { + path = FileUtil.slashify(path); + } + // Character '\' can be use for Unicode representation, e.g. \\u000A is new line + path = path.replace("\\", "\\\\"); + println("// source: " + path); println(""); } @@ -423,20 +434,6 @@ private void emitUserCode() { } private void emitClassName() { - if (!endsWithJavadoc(scanner.userCode)) { - String path = inputFile.toString(); - // slashify path (avoid backslash u sequence = unicode escape) - if (File.separatorChar != '/') { - path = path.replace(File.separatorChar, '/'); - } - - println("/**"); - println(" * This class is a scanner generated by "); - println(" * JFlex " + Main.version); - println(" * from the specification file " + path + ""); - println(" */"); - } - if (scanner.isPublic) print("public "); if (scanner.isAbstract) print("abstract "); @@ -706,7 +703,7 @@ private void emitRowMapArray() { private void emitAttributes() { println(" /**"); - println(" * ZZ_ATTRIBUTE[aState] contains the attributes of state aState"); + println(" * ZZ_ATTRIBUTE[aState] contains the attributes of state {@code aState}"); println(" */"); CountEmitter e = new CountEmitter("Attribute"); diff --git a/jflex/src/main/java/jflex/ErrorMessages.java b/jflex/src/main/java/jflex/ErrorMessages.java index 28b4dac65..7cad6f261 100644 --- a/jflex/src/main/java/jflex/ErrorMessages.java +++ b/jflex/src/main/java/jflex/ErrorMessages.java @@ -61,172 +61,172 @@ public static String get(ErrorMessages msg, Object... args) { } // typesafe enumeration (generated, do not edit) - /** Constant UNTERMINATED_STR */ + /** Constant {@code UNTERMINATED_STR} */ public static ErrorMessages UNTERMINATED_STR = new ErrorMessages("UNTERMINATED_STR"); - /** Constant EOF_WO_ACTION */ + /** Constant {@code EOF_WO_ACTION} */ public static ErrorMessages EOF_WO_ACTION = new ErrorMessages("EOF_WO_ACTION"); - /** Constant UNKNOWN_OPTION */ + /** Constant {@code UNKNOWN_OPTION} */ public static ErrorMessages UNKNOWN_OPTION = new ErrorMessages("UNKNOWN_OPTION"); - /** Constant UNEXPECTED_CHAR */ + /** Constant {@code UNEXPECTED_CHAR} */ public static ErrorMessages UNEXPECTED_CHAR = new ErrorMessages("UNEXPECTED_CHAR"); - /** Constant UNEXPECTED_NL */ + /** Constant {@code UNEXPECTED_NL} */ public static ErrorMessages UNEXPECTED_NL = new ErrorMessages("UNEXPECTED_NL"); - /** Constant LEXSTATE_UNDECL */ + /** Constant {@code LEXSTATE_UNDECL} */ public static ErrorMessages LEXSTATE_UNDECL = new ErrorMessages("LEXSTATE_UNDECL"); - /** Constant REPEAT_ZERO */ + /** Constant {@code REPEAT_ZERO} */ public static ErrorMessages REPEAT_ZERO = new ErrorMessages("REPEAT_ZERO"); - /** Constant REPEAT_GREATER */ + /** Constant {@code REPEAT_GREATER} */ public static ErrorMessages REPEAT_GREATER = new ErrorMessages("REPEAT_GREATER"); - /** Constant REGEXP_EXPECTED */ + /** Constant {@code REGEXP_EXPECTED} */ public static ErrorMessages REGEXP_EXPECTED = new ErrorMessages("REGEXP_EXPECTED"); - /** Constant MACRO_UNDECL */ + /** Constant {@code MACRO_UNDECL} */ public static ErrorMessages MACRO_UNDECL = new ErrorMessages("MACRO_UNDECL"); - /** Constant CHARSET_2_SMALL */ + /** Constant {@code CHARSET_2_SMALL} */ public static ErrorMessages CHARSET_2_SMALL = new ErrorMessages("CHARSET_2_SMALL"); - /** Constant CS2SMALL_STRING */ + /** Constant {@code CS2SMALL_STRING} */ public static ErrorMessages CS2SMALL_STRING = new ErrorMessages("CS2SMALL_STRING"); - /** Constant CS2SMALL_CHAR */ + /** Constant {@code CS2SMALL_CHAR} */ public static ErrorMessages CS2SMALL_CHAR = new ErrorMessages("CS2SMALL_CHAR"); - /** Constant CHARCLASS_MACRO */ + /** Constant {@code CHARCLASS_MACRO} */ public static ErrorMessages CHARCLASS_MACRO = new ErrorMessages("CHARCLASS_MACRO"); - /** Constant UNKNOWN_SYNTAX */ + /** Constant {@code UNKNOWN_SYNTAX} */ public static ErrorMessages UNKNOWN_SYNTAX = new ErrorMessages("UNKNOWN_SYNTAX"); - /** Constant SYNTAX_ERROR */ + /** Constant {@code SYNTAX_ERROR} */ public static ErrorMessages SYNTAX_ERROR = new ErrorMessages("SYNTAX_ERROR"); - /** Constant NOT_AT_BOL */ + /** Constant {@code NOT_AT_BOL} */ public static ErrorMessages NOT_AT_BOL = new ErrorMessages("NOT_AT_BOL"); - /** Constant EOF_IN_ACTION */ + /** Constant {@code EOF_IN_ACTION} */ public static ErrorMessages EOF_IN_ACTION = new ErrorMessages("EOF_IN_ACTION"); - /** Constant EOF_IN_COMMENT */ + /** Constant {@code EOF_IN_COMMENT} */ public static ErrorMessages EOF_IN_COMMENT = new ErrorMessages("EOF_IN_COMMENT"); - /** Constant EOF_IN_STRING */ + /** Constant {@code EOF_IN_STRING} */ public static ErrorMessages EOF_IN_STRING = new ErrorMessages("EOF_IN_STRING"); - /** Constant EOF_IN_MACROS */ + /** Constant {@code EOF_IN_MACROS} */ public static ErrorMessages EOF_IN_MACROS = new ErrorMessages("EOF_IN_MACROS"); - /** Constant EOF_IN_STATES */ + /** Constant {@code EOF_IN_STATES} */ public static ErrorMessages EOF_IN_STATES = new ErrorMessages("EOF_IN_STATES"); - /** Constant EOF_IN_REGEXP */ + /** Constant {@code EOF_IN_REGEXP} */ public static ErrorMessages EOF_IN_REGEXP = new ErrorMessages("EOF_IN_REGEXP"); - /** Constant UNEXPECTED_EOF */ + /** Constant {@code UNEXPECTED_EOF} */ public static ErrorMessages UNEXPECTED_EOF = new ErrorMessages("UNEXPECTED_EOF"); - /** Constant NO_LEX_SPEC */ + /** Constant {@code NO_LEX_SPEC} */ public static ErrorMessages NO_LEX_SPEC = new ErrorMessages("NO_LEX_SPEC"); - /** Constant NO_LAST_ACTION */ + /** Constant {@code NO_LAST_ACTION} */ public static ErrorMessages NO_LAST_ACTION = new ErrorMessages("NO_LAST_ACTION"); - /** Constant NO_DIRECTORY */ + /** Constant {@code NO_DIRECTORY} */ public static ErrorMessages NO_DIRECTORY = new ErrorMessages("NO_DIRECTORY"); - /** Constant NO_SKEL_FILE */ + /** Constant {@code NO_SKEL_FILE} */ public static ErrorMessages NO_SKEL_FILE = new ErrorMessages("NO_SKEL_FILE"); - /** Constant WRONG_SKELETON */ + /** Constant {@code WRONG_SKELETON} */ public static ErrorMessages WRONG_SKELETON = new ErrorMessages("WRONG_SKELETON"); - /** Constant OUT_OF_MEMORY */ + /** Constant {@code OUT_OF_MEMORY} */ public static ErrorMessages OUT_OF_MEMORY = new ErrorMessages("OUT_OF_MEMORY"); - /** Constant QUIL_INITTHROW */ + /** Constant {@code QUIL_INITTHROW} */ public static ErrorMessages QUIL_INITTHROW = new ErrorMessages("QUIL_INITTHROW"); - /** Constant QUIL_EOFTHROW */ + /** Constant {@code QUIL_EOFTHROW} */ public static ErrorMessages QUIL_EOFTHROW = new ErrorMessages("QUIL_EOFTHROW"); - /** Constant QUIL_YYLEXTHROW */ + /** Constant {@code QUIL_YYLEXTHROW} */ public static ErrorMessages QUIL_YYLEXTHROW = new ErrorMessages("QUIL_YYLEXTHROW"); - /** Constant ZERO_STATES */ + /** Constant {@code ZERO_STATES} */ public static ErrorMessages ZERO_STATES = new ErrorMessages("ZERO_STATES"); - /** Constant NO_BUFFER_SIZE */ + /** Constant {@code NO_BUFFER_SIZE} */ public static ErrorMessages NO_BUFFER_SIZE = new ErrorMessages("NO_BUFFER_SIZE"); - /** Constant NOT_READABLE */ + /** Constant {@code NOT_READABLE} */ public static ErrorMessages NOT_READABLE = new ErrorMessages("NOT_READABLE"); - /** Constant FILE_CYCLE */ + /** Constant {@code FILE_CYCLE} */ public static ErrorMessages FILE_CYCLE = new ErrorMessages("FILE_CYCLE"); - /** Constant FILE_WRITE */ + /** Constant {@code FILE_WRITE} */ public static ErrorMessages FILE_WRITE = new ErrorMessages("FILE_WRITE"); - /** Constant QUIL_SCANERROR */ + /** Constant {@code QUIL_SCANERROR} */ public static ErrorMessages QUIL_SCANERROR = new ErrorMessages("QUIL_SCANERROR"); - /** Constant NEVER_MATCH */ + /** Constant {@code NEVER_MATCH} */ public static ErrorMessages NEVER_MATCH = new ErrorMessages("NEVER_MATCH"); - /** Constant QUIL_THROW */ + /** Constant {@code QUIL_THROW} */ public static ErrorMessages QUIL_THROW = new ErrorMessages("QUIL_THROW"); - /** Constant EOL_IN_CHARCLASS */ + /** Constant {@code EOL_IN_CHARCLASS} */ public static ErrorMessages EOL_IN_CHARCLASS = new ErrorMessages("EOL_IN_CHARCLASS"); - /** Constant QUIL_CUPSYM */ + /** Constant {@code QUIL_CUPSYM} */ public static ErrorMessages QUIL_CUPSYM = new ErrorMessages("QUIL_CUPSYM"); - /** Constant CUPSYM_AFTER_CUP */ + /** Constant {@code CUPSYM_AFTER_CUP} */ public static ErrorMessages CUPSYM_AFTER_CUP = new ErrorMessages("CUPSYM_AFTER_CUP"); - /** Constant ALREADY_RUNNING */ + /** Constant {@code ALREADY_RUNNING} */ public static ErrorMessages ALREADY_RUNNING = new ErrorMessages("ALREADY_RUNNING"); - /** Constant CANNOT_READ_SKEL */ + /** Constant {@code CANNOT_READ_SKEL} */ public static ErrorMessages CANNOT_READ_SKEL = new ErrorMessages("CANNOT_READ_SKEL"); - /** Constant READING_SKEL */ + /** Constant {@code READING_SKEL} */ public static ErrorMessages READING_SKEL = new ErrorMessages("READING_SKEL"); - /** Constant SKEL_IO_ERROR */ + /** Constant {@code SKEL_IO_ERROR} */ public static ErrorMessages SKEL_IO_ERROR = new ErrorMessages("SKEL_IO_ERROR"); - /** Constant SKEL_IO_ERROR_DEFAULT */ + /** Constant {@code SKEL_IO_ERROR_DEFAULT} */ public static ErrorMessages SKEL_IO_ERROR_DEFAULT = new ErrorMessages("SKEL_IO_ERROR_DEFAULT"); - /** Constant READING */ + /** Constant {@code READING} */ public static ErrorMessages READING = new ErrorMessages("READING"); - /** Constant CANNOT_OPEN */ + /** Constant {@code CANNOT_OPEN} */ public static ErrorMessages CANNOT_OPEN = new ErrorMessages("CANNOT_OPEN"); - /** Constant NFA_IS */ + /** Constant {@code NFA_IS} */ public static ErrorMessages NFA_IS = new ErrorMessages("NFA_IS"); - /** Constant NFA_STATES */ + /** Constant {@code NFA_STATES} */ public static ErrorMessages NFA_STATES = new ErrorMessages("NFA_STATES"); - /** Constant DFA_TOOK */ + /** Constant {@code DFA_TOOK} */ public static ErrorMessages DFA_TOOK = new ErrorMessages("DFA_TOOK"); - /** Constant DFA_IS */ + /** Constant {@code DFA_IS} */ public static ErrorMessages DFA_IS = new ErrorMessages("DFA_IS"); - /** Constant MIN_TOOK */ + /** Constant {@code MIN_TOOK} */ public static ErrorMessages MIN_TOOK = new ErrorMessages("MIN_TOOK"); - /** Constant MIN_DFA_IS */ + /** Constant {@code MIN_DFA_IS} */ public static ErrorMessages MIN_DFA_IS = new ErrorMessages("MIN_DFA_IS"); - /** Constant WRITE_TOOK */ + /** Constant {@code WRITE_TOOK} */ public static ErrorMessages WRITE_TOOK = new ErrorMessages("WRITE_TOOK"); - /** Constant TOTAL_TIME */ + /** Constant {@code TOTAL_TIME} */ public static ErrorMessages TOTAL_TIME = new ErrorMessages("TOTAL_TIME"); - /** Constant IO_ERROR */ + /** Constant {@code IO_ERROR} */ public static ErrorMessages IO_ERROR = new ErrorMessages("IO_ERROR"); - /** Constant THIS_IS_JFLEX */ + /** Constant {@code THIS_IS_JFLEX} */ public static ErrorMessages THIS_IS_JFLEX = new ErrorMessages("THIS_IS_JFLEX"); - /** Constant UNKNOWN_COMMANDLINE */ + /** Constant {@code UNKNOWN_COMMANDLINE} */ public static ErrorMessages UNKNOWN_COMMANDLINE = new ErrorMessages("UNKNOWN_COMMANDLINE"); - /** Constant MACRO_CYCLE */ + /** Constant {@code MACRO_CYCLE} */ public static ErrorMessages MACRO_CYCLE = new ErrorMessages("MACRO_CYCLE"); - /** Constant MACRO_DEF_MISSING */ + /** Constant {@code MACRO_DEF_MISSING} */ public static ErrorMessages MACRO_DEF_MISSING = new ErrorMessages("MACRO_DEF_MISSING"); - /** Constant PARSING_TOOK */ + /** Constant {@code PARSING_TOOK} */ public static ErrorMessages PARSING_TOOK = new ErrorMessages("PARSING_TOOK"); - /** Constant NFA_TOOK */ + /** Constant {@code NFA_TOOK} */ public static ErrorMessages NFA_TOOK = new ErrorMessages("NFA_TOOK"); - /** Constant LOOKAHEAD_NEEDS_ACTION */ + /** Constant {@code LOOKAHEAD_NEEDS_ACTION} */ public static ErrorMessages LOOKAHEAD_NEEDS_ACTION = new ErrorMessages("LOOKAHEAD_NEEDS_ACTION"); - /** Constant EMPTY_MATCH */ + /** Constant {@code EMPTY_MATCH} */ public static ErrorMessages EMPTY_MATCH = new ErrorMessages("EMPTY_MATCH"); - /** Constant EMPTY_MATCH_LOOK */ + /** Constant {@code EMPTY_MATCH_LOOK} */ public static ErrorMessages EMPTY_MATCH_LOOK = new ErrorMessages("EMPTY_MATCH_LOOK"); - /** Constant CTOR_ARG */ + /** Constant {@code CTOR_ARG} */ public static ErrorMessages CTOR_ARG = new ErrorMessages("CTOR_ARG"); - /** Constant CTOR_DEBUG */ + /** Constant {@code CTOR_DEBUG} */ public static ErrorMessages CTOR_DEBUG = new ErrorMessages("CTOR_DEBUG"); - /** Constant INT_AND_TYPE */ + /** Constant {@code INT_AND_TYPE} */ public static ErrorMessages INT_AND_TYPE = new ErrorMessages("INT_AND_TYPE"); - /** Constant UNSUPPORTED_UNICODE_VERSION */ + /** Constant {@code UNSUPPORTED_UNICODE_VERSION} */ public static ErrorMessages UNSUPPORTED_UNICODE_VERSION = new ErrorMessages("UNSUPPORTED_UNICODE_VERSION"); - /** Constant UNSUPPORTED_UNICODE_VERSION_SUPPORTED_ARE */ + /** Constant {@code UNSUPPORTED_UNICODE_VERSION_SUPPORTED_ARE} */ public static ErrorMessages UNSUPPORTED_UNICODE_VERSION_SUPPORTED_ARE = new ErrorMessages("UNSUPPORTED_UNICODE_VERSION_SUPPORTED_ARE"); - /** Constant INVALID_UNICODE_PROPERTY */ + /** Constant {@code INVALID_UNICODE_PROPERTY} */ public static ErrorMessages INVALID_UNICODE_PROPERTY = new ErrorMessages("INVALID_UNICODE_PROPERTY"); - /** Constant DOT_BAR_NEWLINE_DOES_NOT_MATCH_ALL_CHARS */ + /** Constant {@code DOT_BAR_NEWLINE_DOES_NOT_MATCH_ALL_CHARS} */ public static ErrorMessages DOT_BAR_NEWLINE_DOES_NOT_MATCH_ALL_CHARS = new ErrorMessages("DOT_BAR_NEWLINE_DOES_NOT_MATCH_ALL_CHARS"); - /** Constant PROPS_ARG_REQUIRES_UNICODE_VERSION */ + /** Constant {@code PROPS_ARG_REQUIRES_UNICODE_VERSION} */ public static ErrorMessages PROPS_ARG_REQUIRES_UNICODE_VERSION = new ErrorMessages("PROPS_ARG_REQUIRES_UNICODE_VERSION"); - /** Constant IMPOSSIBLE_CHARCLASS_RANGE */ + /** Constant {@code IMPOSSIBLE_CHARCLASS_RANGE} */ public static ErrorMessages IMPOSSIBLE_CHARCLASS_RANGE = new ErrorMessages("IMPOSSIBLE_CHARCLASS_RANGE"); - /** Constant CODEPOINT_OUT_OF_RANGE */ + /** Constant {@code CODEPOINT_OUT_OF_RANGE} */ public static ErrorMessages CODEPOINT_OUT_OF_RANGE = new ErrorMessages("CODEPOINT_OUT_OF_RANGE"); - /** Constant NO_ENCODING */ + /** Constant {@code NO_ENCODING} */ public static ErrorMessages NO_ENCODING = new ErrorMessages("NO_ENCODING"); - /** Constant CHARSET_NOT_SUPPORTED */ + /** Constant {@code CHARSET_NOT_SUPPORTED} */ public static ErrorMessages CHARSET_NOT_SUPPORTED = new ErrorMessages("CHARSET_NOT_SUPPORTED"); } diff --git a/jflex/src/main/java/jflex/IntCharSet.java b/jflex/src/main/java/jflex/IntCharSet.java index 313fa690d..ff99c6f7b 100644 --- a/jflex/src/main/java/jflex/IntCharSet.java +++ b/jflex/src/main/java/jflex/IntCharSet.java @@ -11,45 +11,35 @@ import java.util.ArrayList; import java.util.List; +import java.util.Objects; import jflex.unicode.UnicodeProperties; /** - * CharSet implemented with intervals - * - *

[fixme: optimizations possible] + * CharSet implemented with intervals. * * @author Gerwin Klein + * @author Régis Décamps * @version JFlex 1.7.1-SNAPSHOT */ +// FIXME: optimizations possible public final class IntCharSet { private static final boolean DEBUG = false; /* invariant: all intervals are disjoint, ordered */ - private List intervals; + private List intervals = new ArrayList<>(); private int pos; - /** Constructor for IntCharSet. */ - public IntCharSet() { - this.intervals = new ArrayList<>(); - } + /** Creates an empty char set. */ + public IntCharSet() {} - /** - * Constructor for IntCharSet. - * - * @param c a int. - */ + /** Creates a char set that contains only the given character. */ public IntCharSet(int c) { this(new Interval(c, c)); } - /** - * Constructor for IntCharSet. - * - * @param interval a {@link jflex.Interval} object. - */ + /** Creates a charset that contains only one interval. */ public IntCharSet(Interval interval) { - this(); intervals.add(interval); } @@ -216,10 +206,24 @@ public boolean contains(int singleChar) { * *

o instanceof Interval */ + @Override public boolean equals(Object o) { + if (!(o instanceof IntCharSet)) { + return false; + } IntCharSet set = (IntCharSet) o; - return intervals.equals(set.intervals); + return Objects.equals(intervals, set.intervals); + } + + @Override + public int hashCode() { + int h = 1; + for (Interval interval : intervals) { + h *= 1000003; + h ^= interval.hashCode(); + } + return h; } private int min(int a, int b) { diff --git a/jflex/src/main/java/jflex/Interval.java b/jflex/src/main/java/jflex/Interval.java index 652d6b82c..cb8861811 100644 --- a/jflex/src/main/java/jflex/Interval.java +++ b/jflex/src/main/java/jflex/Interval.java @@ -13,15 +13,18 @@ * An interval of characters with basic operations. * * @author Gerwin Klein + * @author Régis Décamps * @version JFlex 1.7.1-SNAPSHOT */ public final class Interval { - /* start and end of the interval */ - public int start, end; + /** Start of the interval. */ + public int start; + /** End of the interval. */ + public int end; /** - * Construct a new interval from start to end. + * Construct a new interval from {@code start to end}. * * @param start first character the interval should contain * @param end last character the interval should contain @@ -31,28 +34,23 @@ public Interval(int start, int end) { this.end = end; } - /** - * Copy constructor. - * - * @param other a {@link jflex.Interval} object. - */ public Interval(Interval other) { this.start = other.start; this.end = other.end; } /** - * Return true iff point is contained in this interval. + * Returns {@code true} iff {@code point} is contained in this interval. * * @param point the character to check - * @return whether the codepoint is contained in the interval. + * @return whether the code point is contained in the interval. */ public boolean contains(int point) { return start <= point && end >= point; } /** - * Return true iff this interval completely contains the other one. + * Return {@code true} iff this interval completely contains the other one. * * @param other the other interval * @return whether this interval completely contains the other one. @@ -61,11 +59,8 @@ public boolean contains(Interval other) { return this.start <= other.start && this.end >= other.end; } - /** - * {@inheritDoc} - * - *

Return true if o is an interval with the same borders. - */ + /** Returns {@code true} if {@code o} is an interval with the same borders. */ + @Override public boolean equals(Object o) { if (o == this) return true; if (!(o instanceof Interval)) return false; @@ -74,6 +69,16 @@ public boolean equals(Object o) { return other.start == this.start && other.end == this.end; } + @Override + public int hashCode() { + int h = 1; + h *= 1000003; + h ^= start; + h *= 1000003; + h ^= end; + return h; + } + /** * Set a new last character * @@ -99,15 +104,15 @@ public void setStart(int start) { */ private static boolean isPrintable(int c) { // fixme: should make unicode test here - return c > 31 && c < 127; + return 31 < c && c < 127; } /** - * Get a String representation of this interval. + * Returns a String representation of this interval. * - * @return a string "[start-end]" or "[start]" (if there is only one - * character in the interval) where start and end are either a - * number (the character code) or something of the from 'a'. + * @return a string "{@code [start-end]}" or "{@code [start]}" (if there is only one character in + * the interval) where {@code start} and {@code end} are either a number (the character code) + * or something of the from {@code 'a'}. */ public String toString() { StringBuilder result = new StringBuilder("["); diff --git a/jflex/src/main/java/jflex/LexGenerator.java b/jflex/src/main/java/jflex/LexGenerator.java new file mode 100644 index 000000000..6d5eeb4f8 --- /dev/null +++ b/jflex/src/main/java/jflex/LexGenerator.java @@ -0,0 +1,132 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * JFlex 1.7.1-SNAPSHOT * + * Copyright (C) 1998-2018 Gerwin Klein * + * All rights reserved. * + * * + * License: BSD * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +package jflex; + +import static jflex.Options.encoding; + +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.Reader; +import java.nio.file.Files; +import java.nio.file.Paths; + +/** + * This is the generator of JFlex, controlling the scanner generation process. + * + * @author Gerwin Klein + * @author Régis Décamps + * @version JFlex 1.7.1-SNAPSHOT + */ +public class LexGenerator { + + /** JFlex version */ + public static final String VERSION = "1.7.1-SNAPSHOT"; // $NON-NLS-1$ + + /** + * Generates a scanner for the specified input file. + * + * @param inputFile a file containing a lexical specification to generate a scanner for. + * @return the file name of the generated Java sources. + */ + public static String generate(File inputFile) { + + Out.resetCounters(); + + Timer totalTime = new Timer(); + Timer time = new Timer(); + + LexScan scanner = null; + LexParse parser = null; + Reader inputReader = null; + + totalTime.start(); + + try { + Out.println(ErrorMessages.READING, inputFile.toString()); + inputReader = + new InputStreamReader(Files.newInputStream(Paths.get(inputFile.toString())), encoding); + scanner = new LexScan(inputReader); + scanner.setFile(inputFile); + parser = new LexParse(scanner); + } catch (IOException e) { + Out.error(ErrorMessages.CANNOT_OPEN, inputFile.toString()); + throw new GeneratorException(); + } + + try { + NFA nfa = (NFA) parser.parse().value; + + Out.checkErrors(); + + if (Options.dump) Out.dump(ErrorMessages.get(ErrorMessages.NFA_IS) + Out.NL + nfa + Out.NL); + + if (Options.dot) nfa.writeDot(Emitter.normalize("nfa.dot", null)); // $NON-NLS-1$ + + Out.println(ErrorMessages.NFA_STATES, nfa.numStates); + + time.start(); + DFA dfa = nfa.getDFA(); + time.stop(); + Out.time(ErrorMessages.DFA_TOOK, time); + + dfa.checkActions(scanner, parser); + + nfa = null; + + if (Options.dump) Out.dump(ErrorMessages.get(ErrorMessages.DFA_IS) + Out.NL + dfa + Out.NL); + + if (Options.dot) dfa.writeDot(Emitter.normalize("dfa-big.dot", null)); // $NON-NLS-1$ + + Out.checkErrors(); + + time.start(); + dfa.minimize(); + time.stop(); + + Out.time(ErrorMessages.MIN_TOOK, time); + + if (Options.dump) Out.dump(ErrorMessages.get(ErrorMessages.MIN_DFA_IS) + Out.NL + dfa); + + if (Options.dot) dfa.writeDot(Emitter.normalize("dfa-min.dot", null)); // $NON-NLS-1$ + + time.start(); + + Emitter emitter = new Emitter(inputFile, parser, dfa); + emitter.emit(); + + time.stop(); + + Out.time(ErrorMessages.WRITE_TOOK, time); + + totalTime.stop(); + + Out.time(ErrorMessages.TOTAL_TIME, totalTime); + return emitter.outputFileName; + } catch (ScannerException e) { + Out.error(e.file, e.message, e.line, e.column); + throw new GeneratorException(); + } catch (MacroException e) { + Out.error(e.getMessage()); + throw new GeneratorException(); + } catch (IOException e) { + Out.error(ErrorMessages.IO_ERROR, e.toString()); + throw new GeneratorException(); + } catch (OutOfMemoryError e) { + Out.error(ErrorMessages.OUT_OF_MEMORY); + throw new GeneratorException(); + } catch (GeneratorException e) { + throw new GeneratorException(); + } catch (Exception e) { + e.printStackTrace(); + throw new GeneratorException(); + } + } +} diff --git a/jflex/src/main/java/jflex/LexicalStates.java b/jflex/src/main/java/jflex/LexicalStates.java index 1b2b3fa3f..2a268999e 100644 --- a/jflex/src/main/java/jflex/LexicalStates.java +++ b/jflex/src/main/java/jflex/LexicalStates.java @@ -54,8 +54,7 @@ public void insert(String name, boolean is_inclusive) { } /** - * Returns the number (code) of a declared state, null if no such state has been - * declared. + * Returns the number (code) of a declared state, {@code null} if no such state has been declared. * * @param name a {@link java.lang.String} object. * @return a {@link java.lang.Integer} object. diff --git a/jflex/src/main/java/jflex/Macros.java b/jflex/src/main/java/jflex/Macros.java index 29f3f503a..dee9e467e 100644 --- a/jflex/src/main/java/jflex/Macros.java +++ b/jflex/src/main/java/jflex/Macros.java @@ -9,10 +9,14 @@ package jflex; +import static jflex.ErrorMessages.MACRO_CYCLE; +import static jflex.ErrorMessages.get; + import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; /** * Symbol table and expander for macros. @@ -41,7 +45,7 @@ public Macros() { * * @param name the name of the new macro * @param definition the definition of the new macro - * @return true, iff the macro name has not been stored before. + * @return {@code true}, iff the macro name has not been stored before. */ public boolean insert(String name, RegExp definition) { @@ -60,7 +64,7 @@ public boolean insert(String name, RegExp definition) { /** * Marks a macro as used. * - * @return true, iff the macro name has been stored before. + * @return {@code true}, iff the macro name has been stored before. * @param name a {@link java.lang.String} object. */ public boolean markUsed(String name) { @@ -70,7 +74,7 @@ public boolean markUsed(String name) { /** * Tests if a macro has been used. * - * @return true, iff the macro has been used in a regular expression. + * @return {@code true}, iff the macro has been used in a regular expression. * @param name a {@link java.lang.String} object. */ public boolean isUsed(String name) { @@ -101,8 +105,8 @@ public List unused() { * one, that doesn't contain any macro usages (expand() called before). * * @param name the name of the macro - * @return the definition of the macro, null if no macro with the specified name has - * been stored. + * @return the definition of the macro, {@code null} if no macro with the specified name has been + * stored. * @see jflex.Macros#expand */ public RegExp getDefinition(String name) { @@ -156,8 +160,7 @@ private RegExp expandMacro(String name, RegExp definition) throws MacroException case sym.MACROUSE: String usename = (String) ((RegExp1) definition).content; - if (name.equals(usename)) - throw new MacroException(ErrorMessages.get(ErrorMessages.MACRO_CYCLE, name)); + if (Objects.equals(name, usename)) throw new MacroException(get(MACRO_CYCLE, name)); RegExp usedef = getDefinition(usename); diff --git a/jflex/src/main/java/jflex/Main.java b/jflex/src/main/java/jflex/Main.java index 87a7e4479..7ea79a449 100644 --- a/jflex/src/main/java/jflex/Main.java +++ b/jflex/src/main/java/jflex/Main.java @@ -9,16 +9,17 @@ package jflex; +import static jflex.ErrorMessages.NO_ENCODING; +import static jflex.Options.setEncoding; +import static jflex.Options.unused_warning; +import static jflex.Out.error; + import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.Reader; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.SortedMap; import java.util.SortedSet; import java.util.TreeMap; @@ -29,114 +30,17 @@ import jflex.unicode.UnicodeProperties; /** - * This is the main class of JFlex controlling the scanner generation process. It is responsible for - * parsing the commandline, getting input files, starting up the GUI if necessary, etc. + * This is the command-line interface. + * + *

It is responsible for parsing the commandline, getting input files, starting up the GUI if + * necessary, etc. and invokes {@link LexGenerator} accordingly. * * @author Gerwin Klein + * @author Régis Décamps * @version JFlex 1.7.1-SNAPSHOT */ public class Main { - /** JFlex version */ - public static final String version = "1.7.1-SNAPSHOT"; // $NON-NLS-1$ - - /** - * Generates a scanner for the specified input file. - * - * @param inputFile a file containing a lexical specification to generate a scanner for. - */ - public static void generate(File inputFile) { - - Out.resetCounters(); - - Timer totalTime = new Timer(); - Timer time = new Timer(); - - LexScan scanner = null; - LexParse parser = null; - Reader inputReader = null; - - totalTime.start(); - - try { - Out.println(ErrorMessages.READING, inputFile.toString()); - inputReader = new InputStreamReader(new FileInputStream(inputFile), Options.encoding); - scanner = new LexScan(inputReader); - scanner.setFile(inputFile); - parser = new LexParse(scanner); - } catch (FileNotFoundException e) { - Out.error(ErrorMessages.CANNOT_OPEN, inputFile.toString()); - throw new GeneratorException(); - } - - try { - NFA nfa = (NFA) parser.parse().value; - - Out.checkErrors(); - - if (Options.dump) Out.dump(ErrorMessages.get(ErrorMessages.NFA_IS) + Out.NL + nfa + Out.NL); - - if (Options.dot) nfa.writeDot(Emitter.normalize("nfa.dot", null)); // $NON-NLS-1$ - - Out.println(ErrorMessages.NFA_STATES, nfa.numStates); - - time.start(); - DFA dfa = nfa.getDFA(); - time.stop(); - Out.time(ErrorMessages.DFA_TOOK, time); - - dfa.checkActions(scanner, parser); - - nfa = null; - - if (Options.dump) Out.dump(ErrorMessages.get(ErrorMessages.DFA_IS) + Out.NL + dfa + Out.NL); - - if (Options.dot) dfa.writeDot(Emitter.normalize("dfa-big.dot", null)); // $NON-NLS-1$ - - Out.checkErrors(); - - time.start(); - dfa.minimize(); - time.stop(); - - Out.time(ErrorMessages.MIN_TOOK, time); - - if (Options.dump) Out.dump(ErrorMessages.get(ErrorMessages.MIN_DFA_IS) + Out.NL + dfa); - - if (Options.dot) dfa.writeDot(Emitter.normalize("dfa-min.dot", null)); // $NON-NLS-1$ - - time.start(); - - Emitter e = new Emitter(inputFile, parser, dfa); - e.emit(); - - time.stop(); - - Out.time(ErrorMessages.WRITE_TOOK, time); - - totalTime.stop(); - - Out.time(ErrorMessages.TOTAL_TIME, totalTime); - } catch (ScannerException e) { - Out.error(e.file, e.message, e.line, e.column); - throw new GeneratorException(); - } catch (MacroException e) { - Out.error(e.getMessage()); - throw new GeneratorException(); - } catch (IOException e) { - Out.error(ErrorMessages.IO_ERROR, e.toString()); - throw new GeneratorException(); - } catch (OutOfMemoryError e) { - Out.error(ErrorMessages.OUT_OF_MEMORY); - throw new GeneratorException(); - } catch (GeneratorException e) { - throw new GeneratorException(); - } catch (Exception e) { - e.printStackTrace(); - throw new GeneratorException(); - } - } - /** * parseOptions. * @@ -144,12 +48,13 @@ public static void generate(File inputFile) { * @return a {@link java.util.List} object. * @throws jflex.SilentExit if any. */ - public static List parseOptions(String argv[]) throws SilentExit { + private static List parseOptions(String argv[]) throws SilentExit { List files = new ArrayList<>(); for (int i = 0; i < argv.length; i++) { - if (argv[i].equals("-d") || argv[i].equals("--outdir")) { // $NON-NLS-1$ //$NON-NLS-2$ + if (Objects.equals(argv[i], "-d") + || Objects.equals(argv[i], "--outdir")) { // $NON-NLS-1$ //$NON-NLS-2$ if (++i >= argv.length) { Out.error(ErrorMessages.NO_DIRECTORY); throw new GeneratorException(); @@ -158,7 +63,8 @@ public static List parseOptions(String argv[]) throws SilentExit { continue; } - if (argv[i].equals("--skel") || argv[i].equals("-skel")) { // $NON-NLS-1$ //$NON-NLS-2$ + if (Objects.equals(argv[i], "--skel") + || Objects.equals(argv[i], "-skel")) { // $NON-NLS-1$ //$NON-NLS-2$ if (++i >= argv.length) { Out.error(ErrorMessages.NO_SKEL_FILE); throw new GeneratorException(); @@ -168,104 +74,113 @@ public static List parseOptions(String argv[]) throws SilentExit { continue; } - if (argv[i].equals("--encoding")) { + if (Objects.equals(argv[i], "--encoding")) { if (++i >= argv.length) { - Out.error(ErrorMessages.NO_ENCODING); + error(NO_ENCODING); throw new GeneratorException(); } - Options.setEncoding(argv[i]); + setEncoding(argv[i]); continue; } - if (argv[i].equals("-jlex") || argv[i].equals("--jlex")) { // $NON-NLS-1$ //$NON-NLS-2$ + if (Objects.equals(argv[i], "-jlex") + || Objects.equals(argv[i], "--jlex")) { // $NON-NLS-1$ //$NON-NLS-2$ Options.jlex = true; continue; } - if (argv[i].equals("-v") - || argv[i].equals("--verbose") - || argv[i].equals("-verbose")) { // $NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + if (Objects.equals(argv[i], "-v") + || Objects.equals(argv[i], "--verbose") + || Objects.equals(argv[i], "-verbose")) { // $NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ Options.verbose = true; Options.progress = true; Options.unused_warning = true; continue; } - if (argv[i].equals("-q") - || argv[i].equals("--quiet") - || argv[i].equals("-quiet")) { // $NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + if (Objects.equals(argv[i], "-q") + || Objects.equals(argv[i], "--quiet") + || Objects.equals(argv[i], "-quiet")) { // $NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ Options.verbose = false; Options.progress = false; Options.unused_warning = false; continue; } - if (argv[i].equals("--warn-unused")) { // $NON-NLS-1$ - Options.unused_warning = true; + if (Objects.equals(argv[i], "--warn-unused")) { // $NON-NLS-1$ + unused_warning = true; continue; } - if (argv[i].equals("--no-warn-unused")) { // $NON-NLS-1$ - Options.unused_warning = false; + if (Objects.equals(argv[i], "--no-warn-unused")) { // $NON-NLS-1$ + unused_warning = false; continue; } - if (argv[i].equals("--dump") || argv[i].equals("-dump")) { // $NON-NLS-1$ //$NON-NLS-2$ + if (Objects.equals(argv[i], "--dump") + || Objects.equals(argv[i], "-dump")) { // $NON-NLS-1$ //$NON-NLS-2$ Options.dump = true; continue; } - if (argv[i].equals("--time") || argv[i].equals("-time")) { // $NON-NLS-1$ //$NON-NLS-2$ + if (Objects.equals(argv[i], "--time") + || Objects.equals(argv[i], "-time")) { // $NON-NLS-1$ //$NON-NLS-2$ Options.time = true; continue; } - if (argv[i].equals("--version") || argv[i].equals("-version")) { // $NON-NLS-1$ //$NON-NLS-2$ - Out.println(ErrorMessages.THIS_IS_JFLEX, version); + if (Objects.equals(argv[i], "--version") + || Objects.equals(argv[i], "-version")) { // $NON-NLS-1$ //$NON-NLS-2$ + Out.println(ErrorMessages.THIS_IS_JFLEX, LexGenerator.VERSION); throw new SilentExit(0); } - if (argv[i].equals("--dot") || argv[i].equals("-dot")) { // $NON-NLS-1$ //$NON-NLS-2$ + if (Objects.equals(argv[i], "--dot") + || Objects.equals(argv[i], "-dot")) { // $NON-NLS-1$ //$NON-NLS-2$ Options.dot = true; continue; } - if (argv[i].equals("--help") - || argv[i].equals("-h") - || argv[i].equals("/h")) { // $NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + if (Objects.equals(argv[i], "--help") + || Objects.equals(argv[i], "-h") + || Objects.equals(argv[i], "/h")) { // $NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ printUsage(); throw new SilentExit(0); } - if (argv[i].equals("--info") || argv[i].equals("-info")) { // $NON-NLS-1$ //$NON-NLS-2$ + if (Objects.equals(argv[i], "--info") + || Objects.equals(argv[i], "-info")) { // $NON-NLS-1$ //$NON-NLS-2$ Out.printSystemInfo(); throw new SilentExit(0); } - if (argv[i].equals("--nomin") || argv[i].equals("-nomin")) { // $NON-NLS-1$ //$NON-NLS-2$ + if (Objects.equals(argv[i], "--nomin") + || Objects.equals(argv[i], "-nomin")) { // $NON-NLS-1$ //$NON-NLS-2$ Options.no_minimize = true; continue; } - if (argv[i].equals("--pack") || argv[i].equals("-pack")) { // $NON-NLS-1$ //$NON-NLS-2$ + if (Objects.equals(argv[i], "--pack") + || Objects.equals(argv[i], "-pack")) { // $NON-NLS-1$ //$NON-NLS-2$ /* no-op - pack is the only generation method */ continue; } - if (argv[i].equals("--nobak") || argv[i].equals("-nobak")) { // $NON-NLS-1$ //$NON-NLS-2$ + if (Objects.equals(argv[i], "--nobak") + || Objects.equals(argv[i], "-nobak")) { // $NON-NLS-1$ //$NON-NLS-2$ Options.no_backup = true; continue; } - if (argv[i].equals("--legacydot") - || argv[i].equals("-legacydot")) { // $NON-NLS-1$ //$NON-NLS-2$ + if (Objects.equals(argv[i], "--legacydot") + || Objects.equals(argv[i], "-legacydot")) { // $NON-NLS-1$ //$NON-NLS-2$ Options.legacy_dot = true; continue; } - if (argv[i].equals("--uniprops") - || argv[i].equals("-uniprops")) { // $NON-NLS-1$ //$NON-NLS-2$ + if (Objects.equals(argv[i], "--uniprops") + || Objects.equals(argv[i], "-uniprops")) { // $NON-NLS-1$ //$NON-NLS-2$ if (++i >= argv.length) { Out.error( ErrorMessages.PROPS_ARG_REQUIRES_UNICODE_VERSION, UnicodeProperties.UNICODE_VERSIONS); @@ -333,7 +248,7 @@ private static void printUnicodePropertyValuesAndAliases(String unicodeVersion) for (String value : propertyValues) { propertyValuesToAliases.put(value, new TreeSet()); } - for (int i = 0; i < propertyValueAliases.length; i += 2) { + for (int i = 0; i < propertyValueAliases.length - 1; i += 2) { String alias = propertyValueAliases[i]; String value = propertyValueAliases[i + 1]; SortedSet aliases = propertyValuesToAliases.get(value); @@ -357,7 +272,7 @@ private static void printUnicodePropertyValuesAndAliases(String unicodeVersion) } /** Prints the cli usage on stdout. */ - public static void printUsage() { + private static void printUsage() { Out.println(""); // $NON-NLS-1$ Out.println("Usage: jflex "); Out.println(""); @@ -384,7 +299,7 @@ public static void printUsage() { Out.println("--help"); Out.println("-h print this message"); Out.println(""); - Out.println(ErrorMessages.THIS_IS_JFLEX, version); + Out.println(ErrorMessages.THIS_IS_JFLEX, LexGenerator.VERSION); Out.println("Have a nice day!"); } @@ -398,15 +313,17 @@ public static void generate(String argv[]) throws SilentExit { List files = parseOptions(argv); if (files.size() > 0) { - for (File file : files) generate(file); + for (File file : files) { + LexGenerator.generate(file); + } } else { new MainFrame(); } } /** - * Starts the generation process with the files in argv or pops up a window to choose - * a file, when argv doesn't have any file entries. + * Starts the generation process with the files in {@code argv} or pops up a window to choose a + * file, when {@code argv} doesn't have any file entries. * * @param argv the commandline. */ @@ -420,4 +337,7 @@ public static void main(String argv[]) { System.exit(e.exitCode()); } } + + // Only CLI, not meant for instanciation. + private Main() {} } diff --git a/jflex/src/main/java/jflex/NFA.java b/jflex/src/main/java/jflex/NFA.java index 02e114e5a..30f51ad41 100644 --- a/jflex/src/main/java/jflex/NFA.java +++ b/jflex/src/main/java/jflex/NFA.java @@ -16,6 +16,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; /** * Non-deterministic finite automata representation in JFlex. @@ -312,7 +313,7 @@ public void addEpsilonTransition(int start, int dest) { } /** - * Returns true, iff the specified set of states contains a final state. + * Returns {@code true}, iff the specified set of states contains a final state. * * @param set the set of states that is tested for final states. */ @@ -325,7 +326,7 @@ private boolean containsFinal(StateSet set) { } /** - * Returns true, iff the specified set of states contains a pushback-state. + * Returns {@code true}, iff the specified set of states contains a pushback-state. * * @param set the set of states that is tested for pushback-states. private boolean * containsPushback(StateSet set) { states.reset(set); @@ -412,12 +413,12 @@ private void epsilonFill() { } /** - * Calculates the set of states that can be reached from another set of states start - * with an specified input character input + * Calculates the set of states that can be reached from another set of states {@code start} with + * an specified input character {@code input} * * @param start the set of states to start from * @param input the input character for which to search the next states - * @return the set of states that are reached from start via input + * @return the set of states that are reached from {@code start via input} */ private StateSet DFAEdge(StateSet start, int input) { // Out.debug(String.format("Calculating DFAEdge for state set "+start+" and input U+04X"), @@ -574,7 +575,7 @@ public String toString() { if (isFinal[i]) { result.append("[FINAL"); String l = action[i].lookString(); - if (!l.equals("")) { + if (!Objects.equals(l, "")) { result.append(", "); result.append(l); } diff --git a/jflex/src/main/java/jflex/Options.java b/jflex/src/main/java/jflex/Options.java index 369008242..5635267c1 100644 --- a/jflex/src/main/java/jflex/Options.java +++ b/jflex/src/main/java/jflex/Options.java @@ -25,6 +25,12 @@ public class Options { /** output directory */ private static File directory; + /** + * The root source directory. + * + *

In a maven project, this is the directory that contains {@code src} and {@code target}. + */ + private static File rootDirectory; /** strict JLex compatibility */ public static boolean jlex; /** don't run minimization algorithm if this is true */ @@ -91,6 +97,18 @@ public static void setDir(File d) { directory = d; } + /** + * Returns the root directory that contains source code. This is the java working (from system + * property {@code user.dir}) by default. + */ + public static File getRootDirectory() { + return rootDirectory; + } + + public static void setRootDirectory(File rootDir) { + rootDirectory = rootDir; + } + /** Sets encoding for input files, and check availability of encoding on this JVM. */ public static void setEncoding(String encodingName) { if (Charset.isSupported(encodingName)) { @@ -104,6 +122,8 @@ public static void setEncoding(String encodingName) { /** Sets all options back to default values. */ public static void setDefaults() { directory = null; + // System.getProperty("user.dir"), the directory where java was run from. + rootDirectory = new File(""); jlex = false; no_minimize = false; no_backup = false; diff --git a/jflex/src/main/java/jflex/Out.java b/jflex/src/main/java/jflex/Out.java index b786cc2d7..be6a69b8c 100644 --- a/jflex/src/main/java/jflex/Out.java +++ b/jflex/src/main/java/jflex/Out.java @@ -46,7 +46,7 @@ public final class Out { private static StdOutWriter out = new StdOutWriter(); /** - * Switches to GUI mode if text is not null + * Switches to GUI mode if {@code text is not null} * * @param text the message TextArea of the JFlex GUI */ @@ -135,8 +135,8 @@ public static void print(String message) { /** * Dump debug information to System.out * - *

Use like this if (Out.DEBUG) Out.debug(message) to save performance during - * normal operation (when DEBUG is turned off). + *

Use like this {@code if (Out.DEBUG) Out.debug(message)} to save performance during normal + * operation (when DEBUG is turned off). * * @param message a {@link java.lang.String} object. */ @@ -403,7 +403,7 @@ public static void printSystemInfo() { err("OS version: " + System.getProperty("os.version")); err("Encoding: " + System.getProperty("file.encoding")); err("Unicode versions: " + UnicodeProperties.UNICODE_VERSIONS); - err("JFlex version: " + Main.version); + err("JFlex version: " + LexGenerator.VERSION); } /** diff --git a/jflex/src/main/java/jflex/PackEmitter.java b/jflex/src/main/java/jflex/PackEmitter.java index 438ac389e..95e2b37e2 100644 --- a/jflex/src/main/java/jflex/PackEmitter.java +++ b/jflex/src/main/java/jflex/PackEmitter.java @@ -12,13 +12,13 @@ import java.util.Locale; /** - * Encodes int arrays as strings. + * Encodes {@code int} arrays as strings. * *

Also splits up strings when longer than 64K in UTF8 encoding. Subclasses emit unpacking code. * - *

Usage protocol: p.emitInit();
- * for each data: p.emitData(data);
- * p.emitUnpack(); + *

Usage protocol: {@code p.emitInit();}
+ * {@code for each data: p.emitData(data);}
+ * {@code p.emitUnpack();} * * @author Gerwin Klein * @version JFlex 1.7.1-SNAPSHOT @@ -64,7 +64,7 @@ public PackEmitter(String name) { /** * Convert array name into all uppercase internal scanner constant name. * - * @return name as a internal constant name. + * @return {@code name} as a internal constant name. * @see PackEmitter#name */ protected String constName() { @@ -156,7 +156,7 @@ protected void nl() { } /** - * Append a unicode/octal escaped character to out buffer. + * Append a unicode/octal escaped character to {@code out} buffer. * * @param c the character to append */ diff --git a/jflex/src/main/java/jflex/RegExp.java b/jflex/src/main/java/jflex/RegExp.java index b64acaec9..649c781a1 100644 --- a/jflex/src/main/java/jflex/RegExp.java +++ b/jflex/src/main/java/jflex/RegExp.java @@ -248,9 +248,9 @@ public final RegExp resolveTilde(Macros macros) { } /** - * Returns a regexp that matches any character: [^] + * Returns a regexp that matches any character: {@code [^]} * - * @return the regexp for [^] + * @return the regexp for {@code [^]} */ public RegExp anyChar() { // FIXME: there is some code duplication here with the parser diff --git a/jflex/src/main/java/jflex/RegExps.java b/jflex/src/main/java/jflex/RegExps.java index d74b2cd48..9288d92a3 100644 --- a/jflex/src/main/java/jflex/RegExps.java +++ b/jflex/src/main/java/jflex/RegExps.java @@ -168,7 +168,7 @@ public boolean isEOF(int num) { } /** - * Getter for the field states. + * Getter for the field {@code states}. * * @param num a int. * @return a {@link java.util.List} object. diff --git a/jflex/src/main/java/jflex/Skeleton.java b/jflex/src/main/java/jflex/Skeleton.java index 04875f39d..866f3e618 100644 --- a/jflex/src/main/java/jflex/Skeleton.java +++ b/jflex/src/main/java/jflex/Skeleton.java @@ -8,13 +8,16 @@ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ package jflex; +import static java.nio.charset.StandardCharsets.UTF_8; + import java.io.BufferedReader; import java.io.File; -import java.io.FileReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; @@ -25,7 +28,7 @@ * portion of generated code (produced in class Emitter) between every two parts of skeleton code. * *

There is a static part (the skeleton code) and state based iterator part to this class. The - * iterator part is used to emit consecutive skeleton sections to some PrintWriter. + * iterator part is used to emit consecutive skeleton sections to some {@code PrintWriter}. * * @see jflex.Emitter * @author Gerwin Klein @@ -99,8 +102,8 @@ public static void readSkelFile(File skeletonFile) { Out.println(ErrorMessages.READING_SKEL, skeletonFile.toString()); - try { - BufferedReader reader = new BufferedReader(new FileReader(skeletonFile)); + try (BufferedReader reader = + Files.newBufferedReader(Paths.get(skeletonFile.toString()), UTF_8)) { readSkel(reader); } catch (IOException e) { Out.error(ErrorMessages.SKEL_IO_ERROR); @@ -186,8 +189,7 @@ public static void readDefault() { throw new GeneratorException(); } - try { - InputStreamReader reader = new InputStreamReader(url.openStream()); + try (InputStreamReader reader = new InputStreamReader(url.openStream())) { readSkel(new BufferedReader(reader)); } catch (IOException e) { Out.error(ErrorMessages.SKEL_IO_ERROR_DEFAULT); diff --git a/jflex/src/main/java/jflex/StateSet.java b/jflex/src/main/java/jflex/StateSet.java index 700b6fa83..cdac34c9e 100644 --- a/jflex/src/main/java/jflex/StateSet.java +++ b/jflex/src/main/java/jflex/StateSet.java @@ -20,7 +20,7 @@ public final class StateSet { private final boolean DEBUG = false; - /** Constant EMPTY */ + /** Constant {@code EMPTY} */ public static final StateSet EMPTY = new StateSet(); static final int BITS = 6; @@ -250,6 +250,9 @@ public boolean containsSet(StateSet set) { /** {@inheritDoc} */ public boolean equals(Object b) { + if (!(b instanceof StateSet)) { + return false; + } int i = 0; int l1, l2; diff --git a/jflex/src/main/java/jflex/StdOutWriter.java b/jflex/src/main/java/jflex/StdOutWriter.java index 32b39f581..274fb6baa 100644 --- a/jflex/src/main/java/jflex/StdOutWriter.java +++ b/jflex/src/main/java/jflex/StdOutWriter.java @@ -25,7 +25,7 @@ public final class StdOutWriter extends PrintWriter { private TextArea text; /** - * approximation of the current column in the text area for auto wrapping at wrap + * approximation of the current column in the text area for auto wrapping at {@code wrap} * characters */ private int col; diff --git a/jflex/src/main/java/jflex/anttask/JFlexTask.java b/jflex/src/main/java/jflex/anttask/JFlexTask.java index 979514cbb..8b70bc4b7 100644 --- a/jflex/src/main/java/jflex/anttask/JFlexTask.java +++ b/jflex/src/main/java/jflex/anttask/JFlexTask.java @@ -16,7 +16,7 @@ import java.io.LineNumberReader; import java.util.regex.Matcher; import java.util.regex.Pattern; -import jflex.Main; +import jflex.LexGenerator; import jflex.Options; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.Task; @@ -69,7 +69,7 @@ public void execute() throws BuildException { File destFile = new File(outputDir, className + ".java"); if (inputFile.lastModified() > destFile.lastModified()) { - Main.generate(inputFile); + LexGenerator.generate(inputFile); if (!Options.verbose) System.out.println("Generated: " + destFile.getName()); } } catch (IOException e1) { @@ -160,7 +160,7 @@ public String getPackage() { } /** - * Getter for the field className. + * Getter for the field {@code className}. * * @return class name of input file * @see #findPackageAndClass() diff --git a/jflex/src/main/java/jflex/gui/GeneratorThread.java b/jflex/src/main/java/jflex/gui/GeneratorThread.java index 119df4815..40e1e9376 100644 --- a/jflex/src/main/java/jflex/gui/GeneratorThread.java +++ b/jflex/src/main/java/jflex/gui/GeneratorThread.java @@ -10,9 +10,10 @@ package jflex.gui; import java.io.File; +import java.util.Objects; import jflex.ErrorMessages; import jflex.GeneratorException; -import jflex.Main; +import jflex.LexGenerator; import jflex.Options; import jflex.Out; @@ -58,10 +59,10 @@ public void run() { running = true; setPriority(MIN_PRIORITY); try { - if (!outputDir.equals("")) { + if (!Objects.equals(outputDir, "")) { Options.setDir(outputDir); } - Main.generate(new File(inputFile)); + LexGenerator.generate(new File(inputFile)); Out.statistics(); parent.generationFinished(true); } catch (GeneratorException e) { diff --git a/jflex/src/main/java/jflex/gui/Handles.java b/jflex/src/main/java/jflex/gui/Handles.java index 1a4755278..0969036ba 100644 --- a/jflex/src/main/java/jflex/gui/Handles.java +++ b/jflex/src/main/java/jflex/gui/Handles.java @@ -17,33 +17,33 @@ */ public interface Handles { - /** Constant FILL=0 */ + /** Constant {@code FILL=0} */ int FILL = 0; - /** Constant TOP=1 */ + /** Constant {@code TOP=1} */ int TOP = 1; - /** Constant TOP_LEFT=TOP */ + /** Constant {@code TOP_LEFT=TOP} */ int TOP_LEFT = TOP; - /** Constant TOP_CENTER=2 */ + /** Constant {@code TOP_CENTER=2} */ int TOP_CENTER = 2; - /** Constant TOP_RIGHT=3 */ + /** Constant {@code TOP_RIGHT=3} */ int TOP_RIGHT = 3; - /** Constant CENTER_LEFT=4 */ + /** Constant {@code CENTER_LEFT=4} */ int CENTER_LEFT = 4; - /** Constant CENTER=5 */ + /** Constant {@code CENTER=5} */ int CENTER = 5; - /** Constant CENTER_CENTER=CENTER */ + /** Constant {@code CENTER_CENTER=CENTER} */ int CENTER_CENTER = CENTER; - /** Constant CENTER_RIGHT=6 */ + /** Constant {@code CENTER_RIGHT=6} */ int CENTER_RIGHT = 6; - /** Constant BOTTOM=7 */ + /** Constant {@code BOTTOM=7} */ int BOTTOM = 7; - /** Constant BOTTOM_LEFT=BOTTOM */ + /** Constant {@code BOTTOM_LEFT=BOTTOM} */ int BOTTOM_LEFT = BOTTOM; - /** Constant BOTTOM_CENTER=8 */ + /** Constant {@code BOTTOM_CENTER=8} */ int BOTTOM_CENTER = 8; - /** Constant BOTTOM_RIGHT=9 */ + /** Constant {@code BOTTOM_RIGHT=9} */ int BOTTOM_RIGHT = 9; } diff --git a/jflex/src/main/java/jflex/gui/MainFrame.java b/jflex/src/main/java/jflex/gui/MainFrame.java index 7dd12986d..9ec5181d4 100644 --- a/jflex/src/main/java/jflex/gui/MainFrame.java +++ b/jflex/src/main/java/jflex/gui/MainFrame.java @@ -17,7 +17,7 @@ import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.io.File; -import jflex.Main; +import jflex.LexGenerator; import jflex.Out; /** @@ -53,7 +53,7 @@ public final class MainFrame extends Frame implements Handles { /** Constructor for MainFrame. */ public MainFrame() { - super("JFlex " + Main.version); + super("JFlex " + LexGenerator.VERSION); buildContent(); addWindowListener( diff --git a/jflex/src/main/java/jflex/io/FileUtil.java b/jflex/src/main/java/jflex/io/FileUtil.java new file mode 100644 index 000000000..7390bd591 --- /dev/null +++ b/jflex/src/main/java/jflex/io/FileUtil.java @@ -0,0 +1,31 @@ +package jflex.io; + +import java.io.File; +import java.io.IOException; + +public class FileUtil { + + /** Returns the path of {@code file} relative to {@code rootDirectory}. */ + public static String getRelativePath(File rootDirectory, File file) { + try { + String rootDir = rootDirectory.getCanonicalPath() + File.separator; + String f = file.getCanonicalPath(); + if (f.startsWith(rootDir)) { + return f.substring(rootDir.length()); + } + } catch (IOException e) { + // fall back to file.getPath() + } + return file.getPath(); + } + + /** + * Replaces the {@code \} by a {@code /} in the path, because backslash is used as an escape + * sequence in Java (e.g. {@code \u1234} is a unicode character. + */ + public static String slashify(String path) { + return path.replace('\\', '/'); + } + + private FileUtil() {} // utility class +} diff --git a/jflex/src/main/java/jflex/unicode/UnicodeProperties.java b/jflex/src/main/java/jflex/unicode/UnicodeProperties.java index 29e104103..07ca5a9bf 100644 --- a/jflex/src/main/java/jflex/unicode/UnicodeProperties.java +++ b/jflex/src/main/java/jflex/unicode/UnicodeProperties.java @@ -12,6 +12,7 @@ import java.util.HashMap; import java.util.Locale; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -41,11 +42,10 @@ * downloaded from unicode.org. * * @author JFlex contributors. - * @version $Id: $Id */ public class UnicodeProperties { - /** Constant UNICODE_VERSIONS="1.1, 1.1.5, 2, 2.0, 2.0.14, 2.1, 2.1.9,"{trunked} */ + /** Constant {@code UNICODE_VERSIONS="1.1, 1.1.5, 2, 2.0, 2.0.14, 2.1, 2.1.9,"{trunked}} */ public static final String UNICODE_VERSIONS = "1.1, 1.1.5, 2, 2.0, 2.0.14, 2.1, 2.1.9, 3, 3.0, 3.0.1, 3.1, 3.1.0, 3.2, 3.2.0, 4, 4.0, 4.0.1, 4.1, 4.1.0, 5, 5.0, 5.0.0, 5.1, 5.1.0, 5.2, 5.2.0, 6, 6.0, 6.0.0, 6.1, 6.1.0, 6.2, 6.2.0, 6.3, 6.3.0, 7, 7.0, 7.0.0, 8, 8.0, 8.0.0, 9, 9.0, 9.0.0"; @@ -159,7 +159,7 @@ private void initCaselessMatches() { */ private void init(String version) throws UnsupportedUnicodeVersionException { - if (version.equals("1.1") || version.equals("1.1.5")) { + if (Objects.equals(version, "1.1") || Objects.equals(version, "1.1.5")) { bind( Unicode_1_1.propertyValues, Unicode_1_1.intervals, @@ -167,7 +167,9 @@ private void init(String version) throws UnsupportedUnicodeVersionException { Unicode_1_1.maximumCodePoint, Unicode_1_1.caselessMatchPartitions, Unicode_1_1.caselessMatchPartitionSize); - } else if (version.equals("2") || version.equals("2.0") || version.equals("2.0.14")) { + } else if (Objects.equals(version, "2") + || Objects.equals(version, "2.0") + || Objects.equals(version, "2.0.14")) { bind( Unicode_2_0.propertyValues, Unicode_2_0.intervals, @@ -175,7 +177,7 @@ private void init(String version) throws UnsupportedUnicodeVersionException { Unicode_2_0.maximumCodePoint, Unicode_2_0.caselessMatchPartitions, Unicode_2_0.caselessMatchPartitionSize); - } else if (version.equals("2.1") || version.equals("2.1.9")) { + } else if (Objects.equals(version, "2.1") || Objects.equals(version, "2.1.9")) { bind( Unicode_2_1.propertyValues, Unicode_2_1.intervals, @@ -183,7 +185,9 @@ private void init(String version) throws UnsupportedUnicodeVersionException { Unicode_2_1.maximumCodePoint, Unicode_2_1.caselessMatchPartitions, Unicode_2_1.caselessMatchPartitionSize); - } else if (version.equals("3") || version.equals("3.0") || version.equals("3.0.1")) { + } else if (Objects.equals(version, "3") + || Objects.equals(version, "3.0") + || Objects.equals(version, "3.0.1")) { bind( Unicode_3_0.propertyValues, Unicode_3_0.intervals, @@ -191,7 +195,7 @@ private void init(String version) throws UnsupportedUnicodeVersionException { Unicode_3_0.maximumCodePoint, Unicode_3_0.caselessMatchPartitions, Unicode_3_0.caselessMatchPartitionSize); - } else if (version.equals("3.1") || version.equals("3.1.0")) { + } else if (Objects.equals(version, "3.1") || Objects.equals(version, "3.1.0")) { bind( Unicode_3_1.propertyValues, Unicode_3_1.intervals, @@ -199,7 +203,7 @@ private void init(String version) throws UnsupportedUnicodeVersionException { Unicode_3_1.maximumCodePoint, Unicode_3_1.caselessMatchPartitions, Unicode_3_1.caselessMatchPartitionSize); - } else if (version.equals("3.2") || version.equals("3.2.0")) { + } else if (Objects.equals(version, "3.2") || Objects.equals(version, "3.2.0")) { bind( Unicode_3_2.propertyValues, Unicode_3_2.intervals, @@ -207,7 +211,9 @@ private void init(String version) throws UnsupportedUnicodeVersionException { Unicode_3_2.maximumCodePoint, Unicode_3_2.caselessMatchPartitions, Unicode_3_2.caselessMatchPartitionSize); - } else if (version.equals("4") || version.equals("4.0") || version.equals("4.0.1")) { + } else if (Objects.equals(version, "4") + || Objects.equals(version, "4.0") + || Objects.equals(version, "4.0.1")) { bind( Unicode_4_0.propertyValues, Unicode_4_0.intervals, @@ -215,7 +221,7 @@ private void init(String version) throws UnsupportedUnicodeVersionException { Unicode_4_0.maximumCodePoint, Unicode_4_0.caselessMatchPartitions, Unicode_4_0.caselessMatchPartitionSize); - } else if (version.equals("4.1") || version.equals("4.1.0")) { + } else if (Objects.equals(version, "4.1") || Objects.equals(version, "4.1.0")) { bind( Unicode_4_1.propertyValues, Unicode_4_1.intervals, @@ -223,7 +229,9 @@ private void init(String version) throws UnsupportedUnicodeVersionException { Unicode_4_1.maximumCodePoint, Unicode_4_1.caselessMatchPartitions, Unicode_4_1.caselessMatchPartitionSize); - } else if (version.equals("5") || version.equals("5.0") || version.equals("5.0.0")) { + } else if (Objects.equals(version, "5") + || Objects.equals(version, "5.0") + || Objects.equals(version, "5.0.0")) { bind( Unicode_5_0.propertyValues, Unicode_5_0.intervals, @@ -231,7 +239,7 @@ private void init(String version) throws UnsupportedUnicodeVersionException { Unicode_5_0.maximumCodePoint, Unicode_5_0.caselessMatchPartitions, Unicode_5_0.caselessMatchPartitionSize); - } else if (version.equals("5.1") || version.equals("5.1.0")) { + } else if (Objects.equals(version, "5.1") || Objects.equals(version, "5.1.0")) { bind( Unicode_5_1.propertyValues, Unicode_5_1.intervals, @@ -239,7 +247,7 @@ private void init(String version) throws UnsupportedUnicodeVersionException { Unicode_5_1.maximumCodePoint, Unicode_5_1.caselessMatchPartitions, Unicode_5_1.caselessMatchPartitionSize); - } else if (version.equals("5.2") || version.equals("5.2.0")) { + } else if (Objects.equals(version, "5.2") || Objects.equals(version, "5.2.0")) { bind( Unicode_5_2.propertyValues, Unicode_5_2.intervals, @@ -247,7 +255,9 @@ private void init(String version) throws UnsupportedUnicodeVersionException { Unicode_5_2.maximumCodePoint, Unicode_5_2.caselessMatchPartitions, Unicode_5_2.caselessMatchPartitionSize); - } else if (version.equals("6") || version.equals("6.0") || version.equals("6.0.0")) { + } else if (Objects.equals(version, "6") + || Objects.equals(version, "6.0") + || Objects.equals(version, "6.0.0")) { bind( Unicode_6_0.propertyValues, Unicode_6_0.intervals, @@ -255,7 +265,7 @@ private void init(String version) throws UnsupportedUnicodeVersionException { Unicode_6_0.maximumCodePoint, Unicode_6_0.caselessMatchPartitions, Unicode_6_0.caselessMatchPartitionSize); - } else if (version.equals("6.1") || version.equals("6.1.0")) { + } else if (Objects.equals(version, "6.1") || Objects.equals(version, "6.1.0")) { bind( Unicode_6_1.propertyValues, Unicode_6_1.intervals, @@ -263,7 +273,7 @@ private void init(String version) throws UnsupportedUnicodeVersionException { Unicode_6_1.maximumCodePoint, Unicode_6_1.caselessMatchPartitions, Unicode_6_1.caselessMatchPartitionSize); - } else if (version.equals("6.2") || version.equals("6.2.0")) { + } else if (Objects.equals(version, "6.2") || Objects.equals(version, "6.2.0")) { bind( Unicode_6_2.propertyValues, Unicode_6_2.intervals, @@ -271,7 +281,7 @@ private void init(String version) throws UnsupportedUnicodeVersionException { Unicode_6_2.maximumCodePoint, Unicode_6_2.caselessMatchPartitions, Unicode_6_2.caselessMatchPartitionSize); - } else if (version.equals("6.3") || version.equals("6.3.0")) { + } else if (Objects.equals(version, "6.3") || Objects.equals(version, "6.3.0")) { bind( Unicode_6_3.propertyValues, Unicode_6_3.intervals, @@ -279,7 +289,9 @@ private void init(String version) throws UnsupportedUnicodeVersionException { Unicode_6_3.maximumCodePoint, Unicode_6_3.caselessMatchPartitions, Unicode_6_3.caselessMatchPartitionSize); - } else if (version.equals("7") || version.equals("7.0") || version.equals("7.0.0")) { + } else if (Objects.equals(version, "7") + || Objects.equals(version, "7.0") + || Objects.equals(version, "7.0.0")) { bind( Unicode_7_0.propertyValues, Unicode_7_0.intervals, @@ -287,7 +299,9 @@ private void init(String version) throws UnsupportedUnicodeVersionException { Unicode_7_0.maximumCodePoint, Unicode_7_0.caselessMatchPartitions, Unicode_7_0.caselessMatchPartitionSize); - } else if (version.equals("8") || version.equals("8.0") || version.equals("8.0.0")) { + } else if (Objects.equals(version, "8") + || Objects.equals(version, "8.0") + || Objects.equals(version, "8.0.0")) { bind( Unicode_8_0.propertyValues, Unicode_8_0.intervals, @@ -295,7 +309,9 @@ private void init(String version) throws UnsupportedUnicodeVersionException { Unicode_8_0.maximumCodePoint, Unicode_8_0.caselessMatchPartitions, Unicode_8_0.caselessMatchPartitionSize); - } else if (version.equals("9") || version.equals("9.0") || version.equals("9.0.0")) { + } else if (Objects.equals(version, "9") + || Objects.equals(version, "9.0") + || Objects.equals(version, "9.0.0")) { bind( Unicode_9_0.propertyValues, Unicode_9_0.intervals, diff --git a/jflex/src/main/java/jflex/unicode/data/Unicode_1_1.java b/jflex/src/main/java/jflex/unicode/data/Unicode_1_1.java index 34d4b462f..b557c22ca 100644 --- a/jflex/src/main/java/jflex/unicode/data/Unicode_1_1.java +++ b/jflex/src/main/java/jflex/unicode/data/Unicode_1_1.java @@ -4,12 +4,11 @@ * Unicode_1_1 class. * * @author JFlex contributors. - * @version $Id: $Id */ public class Unicode_1_1 { - /** Constant maximumCodePoint=0xffff */ + /** Constant {@code maximumCodePoint=0xffff} */ public static final int maximumCodePoint = 0xffff; - /** Constant propertyValues="{alnum, assigned, blank, cc, cn,graph, "{trunked} */ + /** Constant {@code propertyValues="{alnum, assigned, blank, cc, cn,graph, "{trunked}} */ public static final String[] propertyValues = { "alnum", "assigned", "blank", "cc", "cn", "graph", "ll", "lm", "lo", "lu", @@ -18,7 +17,7 @@ public class Unicode_1_1 { "sm", "so", "xdigit", "zl", "zp", "zs" }; - /** Constant intervals="{// Unicode 1.1 property value: {alnum}"{trunked} */ + /** Constant {@code intervals="{// Unicode 1.1 property value: {alnum}"{trunked}} */ public static final String[] intervals = { // Unicode 1.1 property value: {alnum} "\u0030\u0039" @@ -2611,10 +2610,10 @@ public class Unicode_1_1 { // Unicode 1.1 property value: {zs} "\u0020\u0020" + "\u00a0\u00a0" + "\u2000\u200b" + "\u3000\u3000" + "\ufeff\ufeff" }; - /** Constant propertyValueAliases="{}" */ + /** Constant {@code propertyValueAliases="{}"} */ public static final String[] propertyValueAliases = {}; - /** Constant caselessMatchPartitionSize=3 */ + /** Constant {@code caselessMatchPartitionSize=3} */ public static final int caselessMatchPartitionSize = 3; /** * Constant caselessMatchPartitions="\u0041\u0061\000\u0042\u0062\000\u0043\"{trunked} diff --git a/jflex/src/main/java/jflex/unicode/data/Unicode_2_0.java b/jflex/src/main/java/jflex/unicode/data/Unicode_2_0.java index 0500621fc..34469ed7a 100644 --- a/jflex/src/main/java/jflex/unicode/data/Unicode_2_0.java +++ b/jflex/src/main/java/jflex/unicode/data/Unicode_2_0.java @@ -4,12 +4,11 @@ * Unicode_2_0 class. * * @author JFlex contributors. - * @version $Id: $Id */ public class Unicode_2_0 { - /** Constant maximumCodePoint=0xffff */ + /** Constant {@code maximumCodePoint=0xffff} */ public static final int maximumCodePoint = 0xffff; - /** Constant propertyValues="{age=1.1, age=2.0, age=unassigned, alnu"{trunked} */ + /** Constant {@code propertyValues="{age=1.1, age=2.0, age=unassigned, alnu"{trunked}} */ public static final String[] propertyValues = { "age=1.1", "age=2.0", "age=unassigned", "alnum", "alphabetic", "assigned", "bidi=arabicdigit", "bidi=blockseparator", "bidi=commonseparator", @@ -56,7 +55,7 @@ public class Unicode_2_0 { "whitespace", "xdigit", "zerowidth", "zl", "zp", "zs" }; - /** Constant intervals="{// Unicode 2.0 property value: {age=1."{trunked} */ + /** Constant {@code intervals="{// Unicode 2.0 property value: {age=1."{trunked}} */ public static final String[] intervals = { // Unicode 2.0 property value: {age=1.1} "\000\u01f5" @@ -5916,10 +5915,10 @@ public class Unicode_2_0 { // Unicode 2.0 property value: {zs} "\u0020\u0020" + "\u00a0\u00a0" + "\u2000\u200b" + "\u3000\u3000" }; - /** Constant propertyValueAliases="{}" */ + /** Constant {@code propertyValueAliases="{}"} */ public static final String[] propertyValueAliases = {}; - /** Constant caselessMatchPartitionSize=4 */ + /** Constant {@code caselessMatchPartitionSize=4} */ public static final int caselessMatchPartitionSize = 4; /** * Constant caselessMatchPartitions="\u0041\u0061\000\000\u0042\u0062\000\00"{trunked} diff --git a/jflex/src/main/java/jflex/unicode/data/Unicode_2_1.java b/jflex/src/main/java/jflex/unicode/data/Unicode_2_1.java index 3bfff5f5e..72fec44fe 100644 --- a/jflex/src/main/java/jflex/unicode/data/Unicode_2_1.java +++ b/jflex/src/main/java/jflex/unicode/data/Unicode_2_1.java @@ -4,12 +4,11 @@ * Unicode_2_1 class. * * @author JFlex contributors. - * @version $Id: $Id */ public class Unicode_2_1 { - /** Constant maximumCodePoint=0xffff */ + /** Constant {@code maximumCodePoint=0xffff} */ public static final int maximumCodePoint = 0xffff; - /** Constant propertyValues="{age=1.1, age=2.0, age=2.1, age=unassig"{trunked} */ + /** Constant {@code propertyValues="{age=1.1, age=2.0, age=2.1, age=unassig"{trunked}} */ public static final String[] propertyValues = { "age=1.1", "age=2.0", "age=2.1", "age=unassigned", "alnum", "alphabetic", "assigned", "bidi=arabicdigit", "bidi=blockseparator", "bidi=commonseparator", @@ -54,7 +53,7 @@ public class Unicode_2_1 { "space", "terminalpunctuation", "unassignedcodevalue", "whitespace", "xdigit", "zerowidth", "zl", "zp", "zs" }; - /** Constant intervals="{// Unicode 2.1 property value: {age=1."{trunked} */ + /** Constant {@code intervals="{// Unicode 2.1 property value: {age=1."{trunked}} */ public static final String[] intervals = { // Unicode 2.1 property value: {age=1.1} "\000\u01f5" @@ -6401,10 +6400,10 @@ public class Unicode_2_1 { // Unicode 2.1 property value: {zs} "\u0020\u0020" + "\u00a0\u00a0" + "\u2000\u200b" + "\u3000\u3000" }; - /** Constant propertyValueAliases="{}" */ + /** Constant {@code propertyValueAliases="{}"} */ public static final String[] propertyValueAliases = {}; - /** Constant caselessMatchPartitionSize=4 */ + /** Constant {@code caselessMatchPartitionSize=4} */ public static final int caselessMatchPartitionSize = 4; /** * Constant caselessMatchPartitions="\u0041\u0061\000\000\u0042\u0062\000\00"{trunked} diff --git a/jflex/src/main/java/jflex/unicode/data/Unicode_3_0.java b/jflex/src/main/java/jflex/unicode/data/Unicode_3_0.java index ebc121d1a..394cf6b32 100644 --- a/jflex/src/main/java/jflex/unicode/data/Unicode_3_0.java +++ b/jflex/src/main/java/jflex/unicode/data/Unicode_3_0.java @@ -4,12 +4,11 @@ * Unicode_3_0 class. * * @author JFlex contributors. - * @version $Id: $Id */ public class Unicode_3_0 { - /** Constant maximumCodePoint=0x10ffff */ + /** Constant {@code maximumCodePoint=0x10ffff} */ public static final int maximumCodePoint = 0x10ffff; - /** Constant propertyValues="{age=1.1, age=2.0, age=2.1, age=3.0, ag"{trunked} */ + /** Constant {@code propertyValues="{age=1.1, age=2.0, age=2.1, age=3.0, ag"{trunked}} */ public static final String[] propertyValues = { "age=1.1", "age=2.0", "age=2.1", "age=3.0", "age=unassigned", "alnum", "alphabetic", "assigned", "bidi=arabicdigit", "bidi=arabicrighttoleft", @@ -71,7 +70,7 @@ public class Unicode_3_0 { "whitespace", "xdigit", "zerowidth", "zl", "zp", "zs" }; - /** Constant intervals="{// Unicode 3.0 property value: {age=1."{trunked} */ + /** Constant {@code intervals="{// Unicode 3.0 property value: {age=1."{trunked}} */ public static final String[] intervals = { // Unicode 3.0 property value: {age=1.1} "\000\u01f5" @@ -9871,10 +9870,10 @@ public class Unicode_3_0 { + "\u202f\u202f" + "\u3000\u3000" }; - /** Constant propertyValueAliases="{}" */ + /** Constant {@code propertyValueAliases="{}"} */ public static final String[] propertyValueAliases = {}; - /** Constant caselessMatchPartitionSize=4 */ + /** Constant {@code caselessMatchPartitionSize=4} */ public static final int caselessMatchPartitionSize = 4; /** * Constant caselessMatchPartitions="\u0041\u0061\000\000\u0042\u0062\000\00"{trunked} diff --git a/jflex/src/main/java/jflex/unicode/data/Unicode_3_1.java b/jflex/src/main/java/jflex/unicode/data/Unicode_3_1.java index d7d2a3544..02152a971 100644 --- a/jflex/src/main/java/jflex/unicode/data/Unicode_3_1.java +++ b/jflex/src/main/java/jflex/unicode/data/Unicode_3_1.java @@ -4,12 +4,11 @@ * Unicode_3_1 class. * * @author JFlex contributors. - * @version $Id: $Id */ public class Unicode_3_1 { - /** Constant maximumCodePoint=0x10ffff */ + /** Constant {@code maximumCodePoint=0x10ffff} */ public static final int maximumCodePoint = 0x10ffff; - /** Constant propertyValues="{age=1.1, age=2.0, age=2.1, age=3.0, ag"{trunked} */ + /** Constant {@code propertyValues="{age=1.1, age=2.0, age=2.1, age=3.0, ag"{trunked}} */ public static final String[] propertyValues = { "age=1.1", "age=2.0", "age=2.1", "age=3.0", "age=3.1", "age=unassigned", "alnum", "alphabetic", "arabic", "armenian", @@ -74,7 +73,7 @@ public class Unicode_3_1 { "uppercase", "whitespace", "xdigit", "xidcontinue", "xidstart", "yi", "zl", "zp", "zs" }; - /** Constant intervals="{// Unicode 3.1 property value: {age=1."{trunked} */ + /** Constant {@code intervals="{// Unicode 3.1 property value: {age=1."{trunked}} */ public static final String[] intervals = { // Unicode 3.1 property value: {age=1.1} "\000\u01f5" @@ -11092,10 +11091,10 @@ public class Unicode_3_1 { + "\u202f\u202f" + "\u3000\u3000" }; - /** Constant propertyValueAliases="{}" */ + /** Constant {@code propertyValueAliases="{}"} */ public static final String[] propertyValueAliases = {}; - /** Constant caselessMatchPartitionSize=4 */ + /** Constant {@code caselessMatchPartitionSize=4} */ public static final int caselessMatchPartitionSize = 4; /** * Constant caselessMatchPartitions="\u0041\u0061\000\000\u0042\u0062\000\00"{trunked} diff --git a/jflex/src/main/java/jflex/unicode/data/Unicode_3_2.java b/jflex/src/main/java/jflex/unicode/data/Unicode_3_2.java index ec3a92274..7eaffd92c 100644 --- a/jflex/src/main/java/jflex/unicode/data/Unicode_3_2.java +++ b/jflex/src/main/java/jflex/unicode/data/Unicode_3_2.java @@ -4,12 +4,11 @@ * Unicode_3_2 class. * * @author JFlex contributors. - * @version $Id: $Id */ public class Unicode_3_2 { - /** Constant maximumCodePoint=0x10ffff */ + /** Constant {@code maximumCodePoint=0x10ffff} */ public static final int maximumCodePoint = 0x10ffff; - /** Constant propertyValues="{age=1.1, age=2.0, age=2.1, age=3.0, ag"{trunked} */ + /** Constant {@code propertyValues="{age=1.1, age=2.0, age=2.1, age=3.0, ag"{trunked}} */ public static final String[] propertyValues = { "age=1.1", "age=2.0", "age=2.1", "age=3.0", "age=3.1", "age=3.2", "age=unassigned", "alnum", "alphabetic", "arabic", @@ -94,7 +93,7 @@ public class Unicode_3_2 { "xidcontinue", "xidstart", "yi", "zl", "zp", "zs" }; - /** Constant intervals="{// Unicode 3.2 property value: {age=1."{trunked} */ + /** Constant {@code intervals="{// Unicode 3.2 property value: {age=1."{trunked}} */ public static final String[] intervals = { // Unicode 3.2 property value: {age=1.1} "\000\u01f5" @@ -12391,7 +12390,7 @@ public class Unicode_3_2 { + "\u3000\u3000" }; /** - * Constant propertyValueAliases="{ahex, asciihexdigit, alpha, alphabetic"{trunked} + * Constant {@code propertyValueAliases="{ahex, asciihexdigit, alpha, alphabetic"{trunked}} */ public static final String[] propertyValueAliases = { "ahex", "asciihexdigit", "alpha", "alphabetic", @@ -12658,7 +12657,7 @@ public class Unicode_3_2 { "xids", "xidstart", "yiii", "yi", "zyyy", "common" }; - /** Constant caselessMatchPartitionSize=4 */ + /** Constant {@code caselessMatchPartitionSize=4} */ public static final int caselessMatchPartitionSize = 4; /** * Constant caselessMatchPartitions="\u0041\u0061\000\000\u0042\u0062\000\00"{trunked} diff --git a/jflex/src/main/java/jflex/unicode/data/Unicode_4_0.java b/jflex/src/main/java/jflex/unicode/data/Unicode_4_0.java index 157dbdf8c..c2b198e79 100644 --- a/jflex/src/main/java/jflex/unicode/data/Unicode_4_0.java +++ b/jflex/src/main/java/jflex/unicode/data/Unicode_4_0.java @@ -4,12 +4,11 @@ * Unicode_4_0 class. * * @author JFlex contributors. - * @version $Id: $Id */ public class Unicode_4_0 { - /** Constant maximumCodePoint=0x10ffff */ + /** Constant {@code maximumCodePoint=0x10ffff} */ public static final int maximumCodePoint = 0x10ffff; - /** Constant propertyValues="{age=1.1, age=2.0, age=2.1, age=3.0, ag"{trunked} */ + /** Constant {@code propertyValues="{age=1.1, age=2.0, age=2.1, age=3.0, ag"{trunked}} */ public static final String[] propertyValues = { "age=1.1", "age=2.0", "age=2.1", "age=3.0", "age=3.1", "age=3.2", "age=4.0", "age=unassigned", "alnum", "alphabetic", @@ -102,7 +101,7 @@ public class Unicode_4_0 { "xidcontinue", "xidstart", "yi", "zl", "zp", "zs" }; - /** Constant intervals="{// Unicode 4.0 property value: {age=1."{trunked} */ + /** Constant {@code intervals="{// Unicode 4.0 property value: {age=1."{trunked}} */ public static final String[] intervals = { // Unicode 4.0 property value: {age=1.1} "\000\u01f5" @@ -13289,7 +13288,7 @@ public class Unicode_4_0 { + "\u3000\u3000" }; /** - * Constant propertyValueAliases="{ahex, asciihexdigit, alpha, alphabetic"{trunked} + * Constant {@code propertyValueAliases="{ahex, asciihexdigit, alpha, alphabetic"{trunked}} */ public static final String[] propertyValueAliases = { "ahex", "asciihexdigit", "alpha", "alphabetic", @@ -13673,7 +13672,7 @@ public class Unicode_4_0 { "xids", "xidstart", "yiii", "yi", "zyyy", "common" }; - /** Constant caselessMatchPartitionSize=4 */ + /** Constant {@code caselessMatchPartitionSize=4} */ public static final int caselessMatchPartitionSize = 4; /** * Constant caselessMatchPartitions="\u0041\u0061\000\000\u0042\u0062\000\00"{trunked} diff --git a/jflex/src/main/java/jflex/unicode/data/Unicode_4_1.java b/jflex/src/main/java/jflex/unicode/data/Unicode_4_1.java index a734c7bb6..d6fdeda07 100644 --- a/jflex/src/main/java/jflex/unicode/data/Unicode_4_1.java +++ b/jflex/src/main/java/jflex/unicode/data/Unicode_4_1.java @@ -4,12 +4,11 @@ * Unicode_4_1 class. * * @author JFlex contributors. - * @version $Id: $Id */ public class Unicode_4_1 { - /** Constant maximumCodePoint=0x10ffff */ + /** Constant {@code maximumCodePoint=0x10ffff} */ public static final int maximumCodePoint = 0x10ffff; - /** Constant propertyValues="{age=1.1, age=2.0, age=2.1, age=3.0, ag"{trunked} */ + /** Constant {@code propertyValues="{age=1.1, age=2.0, age=2.1, age=3.0, ag"{trunked}} */ public static final String[] propertyValues = { "age=1.1", "age=2.0", "age=2.1", "age=3.0", "age=3.1", "age=3.2", "age=4.0", "age=4.1", "age=unassigned", "alnum", @@ -125,7 +124,7 @@ public class Unicode_4_1 { "xidcontinue", "xidstart", "yi", "zl", "zp", "zs" }; - /** Constant intervals="{// Unicode 4.1 property value: {age=1."{trunked} */ + /** Constant {@code intervals="{// Unicode 4.1 property value: {age=1."{trunked}} */ public static final String[] intervals = { // Unicode 4.1 property value: {age=1.1} "\000\u01f5" @@ -19268,7 +19267,7 @@ public class Unicode_4_1 { + "\u3000\u3000" }; /** - * Constant propertyValueAliases="{ahex, asciihexdigit, alpha, alphabetic"{trunked} + * Constant {@code propertyValueAliases="{ahex, asciihexdigit, alpha, alphabetic"{trunked}} */ public static final String[] propertyValueAliases = { "ahex", "asciihexdigit", "alpha", "alphabetic", @@ -19732,7 +19731,7 @@ public class Unicode_4_1 { "xids", "xidstart", "xpeo", "oldpersian", "yiii", "yi", "zyyy", "common" }; - /** Constant caselessMatchPartitionSize=4 */ + /** Constant {@code caselessMatchPartitionSize=4} */ public static final int caselessMatchPartitionSize = 4; /** * Constant caselessMatchPartitions="\u0041\u0061\000\000\u0042\u0062\000\00"{trunked} diff --git a/jflex/src/main/java/jflex/unicode/data/Unicode_5_0.java b/jflex/src/main/java/jflex/unicode/data/Unicode_5_0.java index ff672efb8..596236226 100644 --- a/jflex/src/main/java/jflex/unicode/data/Unicode_5_0.java +++ b/jflex/src/main/java/jflex/unicode/data/Unicode_5_0.java @@ -4,12 +4,11 @@ * Unicode_5_0 class. * * @author JFlex contributors. - * @version $Id: $Id */ public class Unicode_5_0 { - /** Constant maximumCodePoint=0x10ffff */ + /** Constant {@code maximumCodePoint=0x10ffff} */ public static final int maximumCodePoint = 0x10ffff; - /** Constant propertyValues */ + /** Constant {@code propertyValues} */ public static final String[] propertyValues = { "age=1.1", "age=2.0", "age=2.1", "age=3.0", "age=3.1", "age=3.2", "age=4.0", "age=4.1", "age=5.0", "age=unassigned", @@ -125,7 +124,7 @@ public class Unicode_5_0 { "xdigit", "xidcontinue", "xidstart", "yi", "zl", "zp", "zs" }; - /** Constant intervals="{// Unicode 5.0 property value: {age=1."{trunked} */ + /** Constant {@code intervals="{// Unicode 5.0 property value: {age=1."{trunked}} */ public static final String[] intervals = { // Unicode 5.0 property value: {age=1.1} "\000\u01f5" @@ -20311,7 +20310,7 @@ public class Unicode_5_0 { + "\u3000\u3000" }; /** - * Constant propertyValueAliases="{ahex, asciihexdigit, alpha, alphabetic"{trunked} + * Constant {@code propertyValueAliases="{ahex, asciihexdigit, alpha, alphabetic"{trunked}} */ public static final String[] propertyValueAliases = { "ahex", "asciihexdigit", "alpha", "alphabetic", @@ -20795,7 +20794,7 @@ public class Unicode_5_0 { "yiii", "yi", "zyyy", "common", "zzzz", "unknown" }; - /** Constant caselessMatchPartitionSize=4 */ + /** Constant {@code caselessMatchPartitionSize=4} */ public static final int caselessMatchPartitionSize = 4; /** * Constant caselessMatchPartitions="\u0041\u0061\000\000\u0042\u0062\000\00"{trunked} diff --git a/jflex/src/main/java/jflex/unicode/data/Unicode_5_1.java b/jflex/src/main/java/jflex/unicode/data/Unicode_5_1.java index debb22ada..410233e4c 100644 --- a/jflex/src/main/java/jflex/unicode/data/Unicode_5_1.java +++ b/jflex/src/main/java/jflex/unicode/data/Unicode_5_1.java @@ -4,12 +4,11 @@ * Unicode_5_1 class. * * @author JFlex contributors. - * @version $Id: $Id */ public class Unicode_5_1 { - /** Constant maximumCodePoint=0x10ffff */ + /** Constant {@code maximumCodePoint=0x10ffff} */ public static final int maximumCodePoint = 0x10ffff; - /** Constant propertyValues */ + /** Constant {@code propertyValues} */ public static final String[] propertyValues = { "age=1.1", "age=2.0", "age=2.1", "age=3.0", "age=3.1", "age=3.2", "age=4.0", "age=4.1", "age=5.0", "age=5.1", @@ -140,7 +139,7 @@ public class Unicode_5_1 { "xdigit", "xidcontinue", "xidstart", "yi", "zl", "zp", "zs" }; - /** Constant intervals="{// Unicode 5.1 property value: {age=1."{trunked} */ + /** Constant {@code intervals="{// Unicode 5.1 property value: {age=1."{trunked}} */ public static final String[] intervals = { // Unicode 5.1 property value: {age=1.1} "\000\u01f5" @@ -22707,7 +22706,7 @@ public class Unicode_5_1 { + "\u3000\u3000" }; /** - * Constant propertyValueAliases="{ahex, asciihexdigit, alpha, alphabetic"{trunked} + * Constant {@code propertyValueAliases="{ahex, asciihexdigit, alpha, alphabetic"{trunked}} */ public static final String[] propertyValueAliases = { "ahex", "asciihexdigit", "alpha", "alphabetic", @@ -23253,7 +23252,7 @@ public class Unicode_5_1 { "yiii", "yi", "zyyy", "common", "zzzz", "unknown" }; - /** Constant caselessMatchPartitionSize=4 */ + /** Constant {@code caselessMatchPartitionSize=4} */ public static final int caselessMatchPartitionSize = 4; /** * Constant caselessMatchPartitions="\u0041\u0061\000\000\u0042\u0062\000\00"{trunked} diff --git a/jflex/src/main/java/jflex/unicode/data/Unicode_5_2.java b/jflex/src/main/java/jflex/unicode/data/Unicode_5_2.java index 95db0b8e4..e1593543d 100644 --- a/jflex/src/main/java/jflex/unicode/data/Unicode_5_2.java +++ b/jflex/src/main/java/jflex/unicode/data/Unicode_5_2.java @@ -4,12 +4,11 @@ * Unicode_5_2 class. * * @author JFlex contributors. - * @version $Id: $Id */ public class Unicode_5_2 { - /** Constant maximumCodePoint=0x10ffff */ + /** Constant {@code maximumCodePoint=0x10ffff} */ public static final int maximumCodePoint = 0x10ffff; - /** Constant propertyValues="{age=1.1, age=2.0, age=2.1, age=3.0, ag"{trunked} */ + /** Constant {@code propertyValues="{age=1.1, age=2.0, age=2.1, age=3.0, ag"{trunked}} */ public static final String[] propertyValues = { "age=1.1", "age=2.0", "age=2.1", "age=3.0", "age=3.1", "age=3.2", "age=4.0", "age=4.1", "age=5.0", "age=5.1", @@ -153,7 +152,7 @@ public class Unicode_5_2 { "xdigit", "xidcontinue", "xidstart", "yi", "zl", "zp", "zs" }; - /** Constant intervals="{// Unicode 5.2 property value: {age=1."{trunked} */ + /** Constant {@code intervals="{// Unicode 5.2 property value: {age=1."{trunked}} */ public static final String[] intervals = { // Unicode 5.2 property value: {age=1.1} "\000\u01f5" @@ -27250,7 +27249,7 @@ public class Unicode_5_2 { + "\u3000\u3000" }; /** - * Constant propertyValueAliases="{ahex, asciihexdigit, alpha, alphabetic"{trunked} + * Constant {@code propertyValueAliases="{ahex, asciihexdigit, alpha, alphabetic"{trunked}} */ public static final String[] propertyValueAliases = { "ahex", "asciihexdigit", "alpha", "alphabetic", @@ -27854,7 +27853,7 @@ public class Unicode_5_2 { "zinh", "inherited", "zyyy", "common", "zzzz", "unknown" }; - /** Constant caselessMatchPartitionSize=4 */ + /** Constant {@code caselessMatchPartitionSize=4} */ public static final int caselessMatchPartitionSize = 4; /** * Constant caselessMatchPartitions="\u0041\u0061\000\000\u0042\u0062\000\00"{trunked} diff --git a/jflex/src/main/java/jflex/unicode/data/Unicode_6_0.java b/jflex/src/main/java/jflex/unicode/data/Unicode_6_0.java index e2fc241d6..98340bdcb 100644 --- a/jflex/src/main/java/jflex/unicode/data/Unicode_6_0.java +++ b/jflex/src/main/java/jflex/unicode/data/Unicode_6_0.java @@ -4,12 +4,11 @@ * Unicode_6_0 class. * * @author JFlex contributors. - * @version $Id: $Id */ public class Unicode_6_0 { - /** Constant maximumCodePoint=0x10ffff */ + /** Constant {@code maximumCodePoint=0x10ffff} */ public static final int maximumCodePoint = 0x10ffff; - /** Constant propertyValues="{age=1.1, age=2.0, age=2.1, age=3.0, ag"{trunked} */ + /** Constant {@code propertyValues="{age=1.1, age=2.0, age=2.1, age=3.0, ag"{trunked}} */ public static final String[] propertyValues = { "age=1.1", "age=2.0", "age=2.1", "age=3.0", "age=3.1", "age=3.2", "age=4.0", "age=4.1", "age=5.0", "age=5.1", @@ -199,7 +198,7 @@ public class Unicode_6_0 { "wordbreak=numeric", "wordbreak=other", "xdigit", "xidcontinue", "xidstart", "yi", "zl", "zp", "zs" }; - /** Constant intervals="{// Unicode 6.0 property value: {age=1."{trunked} */ + /** Constant {@code intervals="{// Unicode 6.0 property value: {age=1."{trunked}} */ public static final String[] intervals = { // Unicode 6.0 property value: {age=1.1} "\000\u01f5" @@ -29657,7 +29656,7 @@ public class Unicode_6_0 { + "\u3000\u3000" }; /** - * Constant propertyValueAliases="{ahex, asciihexdigit, alpha, alphabetic"{trunked} + * Constant {@code propertyValueAliases="{ahex, asciihexdigit, alpha, alphabetic"{trunked}} */ public static final String[] propertyValueAliases = { "ahex", "asciihexdigit", "alpha", "alphabetic", @@ -30370,7 +30369,7 @@ public class Unicode_6_0 { "zinh", "inherited", "zyyy", "common", "zzzz", "unknown" }; - /** Constant caselessMatchPartitionSize=4 */ + /** Constant {@code caselessMatchPartitionSize=4} */ public static final int caselessMatchPartitionSize = 4; /** * Constant caselessMatchPartitions="\u0041\u0061\000\000\u0042\u0062\000\00"{trunked} diff --git a/jflex/src/main/java/jflex/unicode/data/Unicode_6_1.java b/jflex/src/main/java/jflex/unicode/data/Unicode_6_1.java index da0367d37..2c72cf6dd 100755 --- a/jflex/src/main/java/jflex/unicode/data/Unicode_6_1.java +++ b/jflex/src/main/java/jflex/unicode/data/Unicode_6_1.java @@ -4,12 +4,11 @@ * Unicode_6_1 class. * * @author JFlex contributors. - * @version $Id: $Id */ public class Unicode_6_1 { - /** Constant maximumCodePoint=0x10ffff */ + /** Constant {@code maximumCodePoint=0x10ffff} */ public static final int maximumCodePoint = 0x10ffff; - /** Constant propertyValues="{age=unassigned, age=v11, age=v20, age="{trunked} */ + /** Constant {@code propertyValues="{age=unassigned, age=v11, age=v20, age="{trunked}} */ public static final String[] propertyValues = { "age=unassigned", "age=v11", "age=v20", "age=v21", "age=v30", "age=v31", "age=v32", "age=v40", "age=v41", "age=v50", @@ -207,7 +206,7 @@ public class Unicode_6_1 { "xidcontinue", "xidstart", "yi", "zl", "zp", "zs" }; - /** Constant intervals="{// Unicode 6.1 property value: {age=un"{trunked} */ + /** Constant {@code intervals="{// Unicode 6.1 property value: {age=un"{trunked}} */ public static final String[] intervals = { // Unicode 6.1 property value: {age=unassigned} "\u0378\u0379" @@ -31618,7 +31617,7 @@ public class Unicode_6_1 { + "\u3000\u3000" }; /** - * Constant propertyValueAliases="{age=1.1, age=v11, age=2.0, age=v20,age"{trunked} + * Constant {@code propertyValueAliases="{age=1.1, age=v11, age=2.0, age=v20,age"{trunked}} */ public static final String[] propertyValueAliases = { "age=1.1", "age=v11", "age=2.0", "age=v20", @@ -32648,7 +32647,7 @@ public class Unicode_6_1 { "zinh", "inherited", "zyyy", "common", "zzzz", "unknown" }; - /** Constant caselessMatchPartitionSize=4 */ + /** Constant {@code caselessMatchPartitionSize=4} */ public static final int caselessMatchPartitionSize = 4; /** * Constant caselessMatchPartitions="\u0041\u0061\000\000\u0042\u0062\000\00"{trunked} diff --git a/jflex/src/main/java/jflex/unicode/data/Unicode_6_2.java b/jflex/src/main/java/jflex/unicode/data/Unicode_6_2.java index 12fad7a8a..30d8cf007 100755 --- a/jflex/src/main/java/jflex/unicode/data/Unicode_6_2.java +++ b/jflex/src/main/java/jflex/unicode/data/Unicode_6_2.java @@ -4,12 +4,11 @@ * Unicode_6_2 class. * * @author JFlex contributors. - * @version $Id: $Id */ public class Unicode_6_2 { - /** Constant maximumCodePoint=0x10ffff */ + /** Constant {@code maximumCodePoint=0x10ffff} */ public static final int maximumCodePoint = 0x10ffff; - /** Constant propertyValues="{age=unassigned, age=v11, age=v20, age="{trunked} */ + /** Constant {@code propertyValues="{age=unassigned, age=v11, age=v20, age="{trunked}} */ public static final String[] propertyValues = { "age=unassigned", "age=v11", "age=v20", "age=v21", "age=v30", "age=v31", "age=v32", "age=v40", "age=v41", "age=v50", @@ -206,7 +205,7 @@ public class Unicode_6_2 { "wordbreak=numeric", "wordbreak=other", "wordbreak=regionalindicator", "xdigit", "xidcontinue", "xidstart", "yi", "zl", "zp", "zs" }; - /** Constant intervals="{// Unicode 6.2 property value: {age=un"{trunked} */ + /** Constant {@code intervals="{// Unicode 6.2 property value: {age=un"{trunked}} */ public static final String[] intervals = { // Unicode 6.2 property value: {age=unassigned} "\u0378\u0379" @@ -32223,7 +32222,7 @@ public class Unicode_6_2 { + "\u3000\u3000" }; /** - * Constant propertyValueAliases="{age=1.1, age=v11, age=2.0, age=v20,age"{trunked} + * Constant {@code propertyValueAliases="{age=1.1, age=v11, age=2.0, age=v20,age"{trunked}} */ public static final String[] propertyValueAliases = { "age=1.1", "age=v11", "age=2.0", "age=v20", @@ -33257,7 +33256,7 @@ public class Unicode_6_2 { "zinh", "inherited", "zyyy", "common", "zzzz", "unknown" }; - /** Constant caselessMatchPartitionSize=4 */ + /** Constant {@code caselessMatchPartitionSize=4} */ public static final int caselessMatchPartitionSize = 4; /** * Constant caselessMatchPartitions="\u0041\u0061\000\000\u0042\u0062\000\00"{trunked} diff --git a/jflex/src/main/java/jflex/unicode/data/Unicode_6_3.java b/jflex/src/main/java/jflex/unicode/data/Unicode_6_3.java index bad8224b9..e6c5d89b6 100755 --- a/jflex/src/main/java/jflex/unicode/data/Unicode_6_3.java +++ b/jflex/src/main/java/jflex/unicode/data/Unicode_6_3.java @@ -4,12 +4,11 @@ * Unicode_6_3 class. * * @author JFlex contributors. - * @version $Id: $Id */ public class Unicode_6_3 { - /** Constant maximumCodePoint=0x10ffff */ + /** Constant {@code maximumCodePoint=0x10ffff} */ public static final int maximumCodePoint = 0x10ffff; - /** Constant propertyValues="{age=unassigned, age=v11, age=v20, age="{trunked} */ + /** Constant {@code propertyValues="{age=unassigned, age=v11, age=v20, age="{trunked}} */ public static final String[] propertyValues = { "age=unassigned", "age=v11", "age=v20", "age=v21", "age=v30", "age=v31", "age=v32", "age=v40", "age=v41", "age=v50", @@ -210,7 +209,7 @@ public class Unicode_6_3 { "wordbreak=regionalindicator", "wordbreak=singlequote", "xdigit", "xidcontinue", "xidstart", "yi", "zl", "zp", "zs" }; - /** Constant intervals="{// Unicode 6.3 property value: {age=un"{trunked} */ + /** Constant {@code intervals="{// Unicode 6.3 property value: {age=un"{trunked}} */ public static final String[] intervals = { // Unicode 6.3 property value: {age=unassigned} "\u0378\u0379" @@ -32808,7 +32807,7 @@ public class Unicode_6_3 { + "\u3000\u3000" }; /** - * Constant propertyValueAliases="{age=1.1, age=v11, age=2.0, age=v20,age"{trunked} + * Constant {@code propertyValueAliases="{age=1.1, age=v11, age=2.0, age=v20,age"{trunked}} */ public static final String[] propertyValueAliases = { "age=1.1", "age=v11", "age=2.0", "age=v20", @@ -33848,7 +33847,7 @@ public class Unicode_6_3 { "zinh", "inherited", "zyyy", "common", "zzzz", "unknown" }; - /** Constant caselessMatchPartitionSize=4 */ + /** Constant {@code caselessMatchPartitionSize=4} */ public static final int caselessMatchPartitionSize = 4; /** * Constant caselessMatchPartitions="\u0041\u0061\000\000\u0042\u0062\000\00"{trunked} diff --git a/jflex/src/main/java/jflex/unicode/data/Unicode_7_0.java b/jflex/src/main/java/jflex/unicode/data/Unicode_7_0.java index 243226625..64db0c5e0 100755 --- a/jflex/src/main/java/jflex/unicode/data/Unicode_7_0.java +++ b/jflex/src/main/java/jflex/unicode/data/Unicode_7_0.java @@ -4,12 +4,11 @@ * Unicode_7_0 class. * * @author JFlex contributors. - * @version $Id: $Id */ public class Unicode_7_0 { - /** Constant maximumCodePoint=0x10ffff */ + /** Constant {@code maximumCodePoint=0x10ffff} */ public static final int maximumCodePoint = 0x10ffff; - /** Constant propertyValues="{age=unassigned, age=v11, age=v20, age="{trunked} */ + /** Constant {@code propertyValues="{age=unassigned, age=v11, age=v20, age="{trunked}} */ public static final String[] propertyValues = { "age=unassigned", "age=v11", "age=v20", "age=v21", "age=v30", "age=v31", "age=v32", "age=v40", "age=v41", "age=v50", @@ -238,7 +237,7 @@ public class Unicode_7_0 { "wordbreak=singlequote", "xdigit", "xidcontinue", "xidstart", "yi", "zl", "zp", "zs" }; - /** Constant intervals="{// Unicode 7.0 property value: {age=un"{trunked} */ + /** Constant {@code intervals="{// Unicode 7.0 property value: {age=un"{trunked}} */ public static final String[] intervals = { // Unicode 7.0 property value: {age=unassigned} "\u0378\u0379" @@ -35636,7 +35635,7 @@ public class Unicode_7_0 { + "\u3000\u3000" }; /** - * Constant propertyValueAliases="{age=1.1, age=v11, age=2.0, age=v20,age"{trunked} + * Constant {@code propertyValueAliases="{age=1.1, age=v11, age=2.0, age=v20,age"{trunked}} */ public static final String[] propertyValueAliases = { "age=1.1", "age=v11", "age=2.0", "age=v20", @@ -36806,7 +36805,7 @@ public class Unicode_7_0 { "zinh", "inherited", "zyyy", "common", "zzzz", "unknown" }; - /** Constant caselessMatchPartitionSize=4 */ + /** Constant {@code caselessMatchPartitionSize=4} */ public static final int caselessMatchPartitionSize = 4; /** * Constant caselessMatchPartitions="\u0041\u0061\000\000\u0042\u0062\000\00"{trunked} diff --git a/jflex/src/main/java/jflex/unicode/data/Unicode_8_0.java b/jflex/src/main/java/jflex/unicode/data/Unicode_8_0.java index b2664d113..f84dddf1b 100755 --- a/jflex/src/main/java/jflex/unicode/data/Unicode_8_0.java +++ b/jflex/src/main/java/jflex/unicode/data/Unicode_8_0.java @@ -4,12 +4,11 @@ * Unicode_8_0 class. * * @author JFlex contributors. - * @version $Id: $Id */ public class Unicode_8_0 { - /** Constant maximumCodePoint=0x10ffff */ + /** Constant {@code maximumCodePoint=0x10ffff} */ public static final int maximumCodePoint = 0x10ffff; - /** Constant propertyValues="{age=unassigned, age=v11, age=v20, age="{trunked} */ + /** Constant {@code propertyValues="{age=unassigned, age=v11, age=v20, age="{trunked}} */ public static final String[] propertyValues = { "age=unassigned", "age=v11", "age=v20", "age=v21", "age=v30", "age=v31", "age=v32", "age=v40", "age=v41", "age=v50", @@ -244,7 +243,7 @@ public class Unicode_8_0 { "xidcontinue", "xidstart", "yi", "zl", "zp", "zs" }; - /** Constant intervals="{// Unicode 8.0 property value: {age=un"{trunked} */ + /** Constant {@code intervals="{// Unicode 8.0 property value: {age=un"{trunked}} */ public static final String[] intervals = { // Unicode 8.0 property value: {age=unassigned} "\u0378\u0379" @@ -36890,7 +36889,7 @@ public class Unicode_8_0 { + "\u3000\u3000" }; /** - * Constant propertyValueAliases="{age=1.1, age=v11, age=2.0, age=v20,age"{trunked} + * Constant {@code propertyValueAliases="{age=1.1, age=v11, age=2.0, age=v20,age"{trunked}} */ public static final String[] propertyValueAliases = { "age=1.1", "age=v11", "age=2.0", "age=v20", @@ -38100,7 +38099,7 @@ public class Unicode_8_0 { "zinh", "inherited", "zyyy", "common", "zzzz", "unknown" }; - /** Constant caselessMatchPartitionSize=4 */ + /** Constant {@code caselessMatchPartitionSize=4} */ public static final int caselessMatchPartitionSize = 4; /** * Constant caselessMatchPartitions="\u0041\u0061\000\000\u0042\u0062\000\00"{trunked} diff --git a/jflex/src/main/java/jflex/unicode/data/Unicode_9_0.java b/jflex/src/main/java/jflex/unicode/data/Unicode_9_0.java index a837a1213..f226fe406 100644 --- a/jflex/src/main/java/jflex/unicode/data/Unicode_9_0.java +++ b/jflex/src/main/java/jflex/unicode/data/Unicode_9_0.java @@ -4,12 +4,11 @@ * Unicode_9_0 class. * * @author JFlex contributors. - * @version $Id: $Id */ public class Unicode_9_0 { - /** Constant maximumCodePoint=0x10ffff */ + /** Constant {@code maximumCodePoint=0x10ffff} */ public static final int maximumCodePoint = 0x10ffff; - /** Constant propertyValues="{adlam, age=unassigned, age=v11, age=v2"{trunked} */ + /** Constant {@code propertyValues="{adlam, age=unassigned, age=v11, age=v2"{trunked}} */ public static final String[] propertyValues = { "adlam", "age=unassigned", "age=v11", "age=v20", "age=v21", "age=v30", "age=v31", "age=v32", "age=v40", "age=v41", @@ -263,7 +262,7 @@ public class Unicode_9_0 { "xidcontinue", "xidstart", "yi", "zl", "zp", "zs" }; - /** Constant intervals="{// Unicode 9.0 property value: {adlam}"{trunked} */ + /** Constant {@code intervals="{// Unicode 9.0 property value: {adlam}"{trunked}} */ public static final String[] intervals = { // Unicode 9.0 property value: {adlam} "\ud83a\udd00\ud83a\udd4a" + "\ud83a\udd50\ud83a\udd59" + "\ud83a\udd5e\ud83a\udd5f", @@ -38521,7 +38520,7 @@ public class Unicode_9_0 { + "\u3000\u3000" }; /** - * Constant propertyValueAliases="{adlm, adlam, age=1.1, age=v11,age=2.0,"{trunked} + * Constant {@code propertyValueAliases="{adlm, adlam, age=1.1, age=v11,age=2.0,"{trunked}} */ public static final String[] propertyValueAliases = { "adlm", "adlam", "age=1.1", "age=v11", @@ -39790,7 +39789,7 @@ public class Unicode_9_0 { "yiii", "yi", "zinh", "inherited", "zyyy", "common", "zzzz", "unknown" }; - /** Constant caselessMatchPartitionSize=4 */ + /** Constant {@code caselessMatchPartitionSize=4} */ public static final int caselessMatchPartitionSize = 4; /** * Constant caselessMatchPartitions="\u0041\u0061\000\000\u0042\u0062\000\00"{trunked} diff --git a/jflex/COPYRIGHT b/jflex/src/main/resources/LICENSE_JFLEX similarity index 62% rename from jflex/COPYRIGHT rename to jflex/src/main/resources/LICENSE_JFLEX index b43a64eeb..8f3b927ff 100644 --- a/jflex/COPYRIGHT +++ b/jflex/src/main/resources/LICENSE_JFLEX @@ -1,21 +1,21 @@ JFlex - Copying, Warranty & License +=================================== -JFlex is free software, since version 1.5 published under the terms of a -BSD-style license. +JFlex is free software, since version 1.5 published under the terms of this +3-clause BSD-style license. There is absolutely NO WARRANTY for JFlex, its code and its documentation. -Copyright (c) Gerwin Klein, Steve Rowe, Régis Décamps. +Copyright (c) Gerwin Klein, Steve Rowe, Régis Decamps. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - * 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. - * Neither the names of the authors nor the names of JFlex contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * 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. + * Neither the names of the authors nor the names of JFlex 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/jflex/src/main/resources/jflex/skeleton.default b/jflex/src/main/resources/jflex/skeleton.default index 9e08fbb0c..11a9d6891 100644 --- a/jflex/src/main/resources/jflex/skeleton.default +++ b/jflex/src/main/resources/jflex/skeleton.default @@ -1,19 +1,24 @@ - /** This character denotes the end of file */ + /** This character denotes the end of file. */ public static final int YYEOF = -1; - /** initial size of the lookahead buffer */ + /** Initial size of the lookahead buffer. */ --- private static final int ZZ_BUFFERSIZE = ...; - /** lexical states */ + // Lexical states. --- lexical states, charmap - /* error codes */ + /** Error code for "Unknown internal scanner error". */ private static final int ZZ_UNKNOWN_ERROR = 0; + /** Error code for "could not match input". */ private static final int ZZ_NO_MATCH = 1; + /** Error code for "pushback value was too large". */ private static final int ZZ_PUSHBACK_2BIG = 2; - /* error messages for the codes above */ + /** + * Error messages for {@link #ZZ_UNKNOWN_ERROR}, {@link #ZZ_NO_MATCH}, and + * {@link #ZZ_PUSHBACK_2BIG} respectively. + */ private static final String ZZ_ERROR_MSG[] = { "Unknown internal scanner error", "Error: could not match input", @@ -21,60 +26,56 @@ }; --- isFinal list - /** the input device */ + /** Input device. */ private java.io.Reader zzReader; - /** the current state of the DFA */ + /** Current state of the DFA. */ private int zzState; - /** the current lexical state */ + /** Current lexical state. */ private int zzLexicalState = YYINITIAL; - /** this buffer contains the current text to be matched and is - the source of the yytext() string */ + /** + * This buffer contains the current text to be matched and is the source of the {@link #yytext()} + * string. + */ private char zzBuffer[] = new char[ZZ_BUFFERSIZE]; - /** the textposition at the last accepting state */ + /** Text position at the last accepting state. */ private int zzMarkedPos; - /** the current text position in the buffer */ + /** Current text position in the buffer. */ private int zzCurrentPos; - /** startRead marks the beginning of the yytext() string in the buffer */ + /** Marks the beginning of the {@link #yytext()} string in the buffer. */ private int zzStartRead; - /** endRead marks the last character in the buffer, that has been read - from input */ + /** Marks the last character in the buffer, that has been read from input. */ private int zzEndRead; - /** number of newlines encountered up to the start of the matched text */ + /** Number of newlines encountered up to the start of the matched text. */ private int yyline; - /** the number of characters up to the start of the matched text */ + /** Number of characters up to the start of the matched text. */ private int yychar; - /** - * the number of characters from the last newline up to the start of the - * matched text - */ + /** Number of characters from the last newline up to the start of the matched text. */ private int yycolumn; - /** - * zzAtBOL == true iff the scanner is currently at the beginning of a line - */ + /** Whether the scanner is currently at the beginning of a line. */ private boolean zzAtBOL = true; - /** zzAtEOF == true iff the scanner is at the EOF */ + /** Whether the scanner is at the end of line. */ private boolean zzAtEOF; - /** denotes if the user-EOF-code has already been executed */ + /** Whether the user-EOF-code has already been executed. */ private boolean zzEOFDone; - - /** - * The number of occupied positions in zzBuffer beyond zzEndRead. - * When a lead/high surrogate has been read from the input stream - * into the final zzBuffer position, this will have a value of 1; - * otherwise, it will have a value of 0. + + /** + * The number of occupied positions in {@link #zzBuffer} beyond {@link #zzEndRead}. + * + *

When a lead/high surrogate has been read from the input stream into the final + * {@link #zzBuffer} position, this will have a value of 1; otherwise, it will have a value of 0. */ private int zzFinalHighSurrogate = 0; @@ -86,9 +87,8 @@ /** * Refills the input buffer. * - * @return false, iff there was new input. - * - * @exception java.io.IOException if any I/O-Error occurs + * @return {@code false} iff there was new input. + * @exception java.io.IOException if any I/O-Error occurs */ private boolean zzRefill() throws java.io.IOException { @@ -98,19 +98,19 @@ zzFinalHighSurrogate = 0; System.arraycopy(zzBuffer, zzStartRead, zzBuffer, 0, - zzEndRead-zzStartRead); + zzEndRead - zzStartRead); /* translate stored positions */ - zzEndRead-= zzStartRead; - zzCurrentPos-= zzStartRead; - zzMarkedPos-= zzStartRead; + zzEndRead -= zzStartRead; + zzCurrentPos -= zzStartRead; + zzMarkedPos -= zzStartRead; zzStartRead = 0; } /* is the buffer big enough? */ if (zzCurrentPos >= zzBuffer.length - zzFinalHighSurrogate) { /* if not: blow it up */ - char newBuffer[] = new char[zzBuffer.length*2]; + char newBuffer[] = new char[zzBuffer.length * 2]; System.arraycopy(zzBuffer, 0, newBuffer, 0, zzBuffer.length); zzBuffer = newBuffer; zzEndRead += zzFinalHighSurrogate; @@ -123,13 +123,14 @@ /* not supposed to occur according to specification of java.io.Reader */ if (numRead == 0) { - throw new java.io.IOException("Reader returned 0 characters. See JFlex examples for workaround."); + throw new java.io.IOException( + "Reader returned 0 characters. See JFlex examples/zero-reader for a workaround."); } if (numRead > 0) { zzEndRead += numRead; - /* If numRead == requested, we might have requested to few chars to - encode a full Unicode character. We assume that a Reader would - otherwise never return half characters. */ + // If numRead == requested, we might have requested to few chars to encode a full Unicode + // character. + // We assume that a Reader would otherwise never return half characters. if (numRead == requested) { if (Character.isHighSurrogate(zzBuffer[zzEndRead - 1])) { --zzEndRead; @@ -149,38 +150,40 @@ * Closes the input stream. */ public final void yyclose() throws java.io.IOException { - zzAtEOF = true; /* indicate end of file */ - zzEndRead = zzStartRead; /* invalidate buffer */ + zzAtEOF = true; // indicate end of file + zzEndRead = zzStartRead; // invalidate buffer - if (zzReader != null) + if (zzReader != null) { zzReader.close(); + } } /** * Resets the scanner to read from a new input stream. - * Does not close the old reader. * - * All internal variables are reset, the old input stream - * cannot be reused (internal buffer is discarded and lost). - * Lexical state is set to ZZ_INITIAL. + *

Does not close the old reader. * - * Internal scan buffer is resized down to its initial length, if it has grown. + *

All internal variables are reset, the old input stream cannot be reused (internal + * buffer is discarded and lost). Lexical state is set to {@code ZZ_INITIAL}. * - * @param reader the new input stream + *

Internal scan buffer is resized down to its initial length, if it has grown. + * + * @param reader The new input stream. */ public final void yyreset(java.io.Reader reader) { zzReader = reader; - zzAtBOL = true; - zzAtEOF = false; + zzAtBOL = true; + zzAtEOF = false; zzEOFDone = false; zzEndRead = zzStartRead = 0; zzCurrentPos = zzMarkedPos = 0; zzFinalHighSurrogate = 0; yyline = yychar = yycolumn = 0; zzLexicalState = YYINITIAL; - if (zzBuffer.length > ZZ_BUFFERSIZE) + if (zzBuffer.length > ZZ_BUFFERSIZE) { zzBuffer = new char[ZZ_BUFFERSIZE]; + } } @@ -193,7 +196,7 @@ /** - * Enters a new lexical state + * Enters a new lexical state. * * @param newState the new lexical state */ @@ -206,23 +209,21 @@ * Returns the text matched by the current regular expression. */ public final String yytext() { - return new String( zzBuffer, zzStartRead, zzMarkedPos-zzStartRead ); + return new String(zzBuffer, zzStartRead, zzMarkedPos-zzStartRead); } /** - * Returns the character at position pos from the - * matched text. + * Returns the character at the given position from the matched text. * - * It is equivalent to yytext().charAt(pos), but faster + *

It is equivalent to {@code yytext().charAt(pos)}, but faster. * - * @param pos the position of the character to fetch. - * A value from 0 to yylength()-1. + * @param position the position of the character to fetch. A value from 0 to {@code yylength()-1}. * - * @return the character at position pos + * @return the character at {@code position}. */ - public final char yycharat(int pos) { - return zzBuffer[zzStartRead+pos]; + public final char yycharat(int position) { + return zzBuffer[zzStartRead + position]; } @@ -235,25 +236,24 @@ /** - * Reports an error that occured while scanning. + * Reports an error that occurred while scanning. * - * In a wellformed scanner (no or only correct usage of - * yypushback(int) and a match-all fallback rule) this method - * will only be called with things that "Can't Possibly Happen". - * If this method is called, something is seriously wrong - * (e.g. a JFlex bug producing a faulty scanner etc.). + *

In a well-formed scanner (no or only correct usage of {@code yypushback(int)} and a + * match-all fallback rule) this method will only be called with things that + * "Can't Possibly Happen". * - * Usual syntax/scanner level error handling should be done - * in error fallback rules. + *

If this method is called, something is seriously wrong (e.g. a JFlex bug producing a faulty + * scanner etc.). * - * @param errorCode the code of the errormessage to display + *

Usual syntax/scanner level error handling should be done in error fallback rules. + * + * @param errorCode the code of the error message to display. */ --- zzScanError declaration String message; try { message = ZZ_ERROR_MSG[errorCode]; - } - catch (ArrayIndexOutOfBoundsException e) { + } catch (ArrayIndexOutOfBoundsException e) { message = ZZ_ERROR_MSG[ZZ_UNKNOWN_ERROR]; } @@ -264,10 +264,10 @@ /** * Pushes the specified amount of characters back into the input stream. * - * They will be read again by then next call of the scanning method + *

They will be read again by then next call of the scanning method. * - * @param number the number of characters to be read again. - * This number must not be greater than yylength()! + * @param number the number of characters to be read again. This number must not be greater than + * {@link #yylength()}. */ --- yypushback decl (contains zzScanError exception) if ( number > yylength() ) @@ -278,12 +278,14 @@ --- zzDoEOF + + /** - * Resumes scanning until the next regular expression is matched, - * the end of input is encountered or an I/O-Error occurs. + * Resumes scanning until the next regular expression is matched, the end of input is encountered + * or an I/O-Error occurs. * - * @return the next token - * @exception java.io.IOException if any I/O-Error occurs + * @return the next token. + * @exception java.io.IOException if any I/O-Error occurs. */ --- yylex declaration int zzInput; @@ -293,8 +295,8 @@ int zzCurrentPosL; int zzMarkedPosL; int zzEndReadL = zzEndRead; - char [] zzBufferL = zzBuffer; - char [] zzCMapL = ZZ_CMAP; + char[] zzBufferL = zzBuffer; + char[] zzCMapL = ZZ_CMAP; --- local declarations diff --git a/jflex/src/test/java/jflex/CharClassesTest.java b/jflex/src/test/java/jflex/CharClassesTest.java index dcca5140b..fd3808ed8 100644 --- a/jflex/src/test/java/jflex/CharClassesTest.java +++ b/jflex/src/test/java/jflex/CharClassesTest.java @@ -88,7 +88,7 @@ public void testCaseless() { try { unicodeProperties = new UnicodeProperties("4.0"); } catch (UnicodeProperties.UnsupportedUnicodeVersionException e) { - assertTrue("Unsupported default Unicode version: " + e, false); + fail("Unsupported default Unicode version: " + e); return; } diff --git a/jflex/src/test/java/jflex/IntCharSetTest.java b/jflex/src/test/java/jflex/IntCharSetTest.java index f637939be..c508d6804 100644 --- a/jflex/src/test/java/jflex/IntCharSetTest.java +++ b/jflex/src/test/java/jflex/IntCharSetTest.java @@ -1,5 +1,6 @@ package jflex; +import java.util.Objects; import junit.framework.TestCase; public class IntCharSetTest extends TestCase { @@ -18,6 +19,7 @@ public void testAddIntCharSet() { IntCharSet original_a = a.copy(); IntCharSet b = new IntCharSet(new Interval((char) 0, (char) 4)); a.add(b); - assertTrue(original_a + " + " + b + " should be " + b + " instead of " + a, a.equals(b)); + assertTrue( + original_a + " + " + b + " should be " + b + " instead of " + a, Objects.equals(a, b)); } } diff --git a/jflex/src/test/java/jflex/RegExpTests.java b/jflex/src/test/java/jflex/RegExpTests.java index de21edc09..2cf93e196 100644 --- a/jflex/src/test/java/jflex/RegExpTests.java +++ b/jflex/src/test/java/jflex/RegExpTests.java @@ -9,6 +9,8 @@ package jflex; +import static jflex.RegExp.revString; + import junit.framework.TestCase; /** @@ -29,7 +31,7 @@ public RegExpTests(String name) { } public void testrevString() { - assertTrue(RegExp.revString("blah").equals("halb")); + assertEquals("halb", revString("blah")); } public void testCharClass() { diff --git a/jflex/src/test/java/jflex/UnicodePropertiesTest.java b/jflex/src/test/java/jflex/UnicodePropertiesTest.java index 20edf4930..6980ac1b6 100644 --- a/jflex/src/test/java/jflex/UnicodePropertiesTest.java +++ b/jflex/src/test/java/jflex/UnicodePropertiesTest.java @@ -9,6 +9,7 @@ package jflex; +import java.util.Objects; import jflex.unicode.UnicodeProperties; import junit.framework.TestCase; @@ -44,7 +45,7 @@ public void testSupportedVersions() { "intervals for 'Lu' property value should have an interval", intervals.numIntervals() > 0); } catch (UnicodeProperties.UnsupportedUnicodeVersionException e) { - assertTrue("Unsupported version '" + version + "' should be supported: " + e, false); + fail("Unsupported version '" + version + "' should be supported: " + e); } } } @@ -52,10 +53,9 @@ public void testSupportedVersions() { public void testUnsupportedVersion() { try { new UnicodeProperties("1.0"); - assertTrue( + fail( "new UnicodeProperties(\"1.0\") should trigger an" - + " UnsupportedUnicodeVersionException, but it did not.", - false); + + " UnsupportedUnicodeVersionException, but it did not."); } catch (UnicodeProperties.UnsupportedUnicodeVersionException e) { // Drop the exception - it is expected. } @@ -75,7 +75,7 @@ public void testDefaultVersion() { "intervals for 'Lu' property value should have an interval", intervals.numIntervals() > 0); } catch (UnicodeProperties.UnsupportedUnicodeVersionException e) { - assertTrue("Default version is unsupported: " + e, false); + fail("Default version is unsupported: " + e); } } @@ -93,7 +93,7 @@ public void testDefaultVersionAliases() { assertTrue("Empty interval set returned for \\p{Lo}", set_1.containsElements()); assertTrue( "\\p{General Category : Other Letter} and \\p{Lo} should" + " return the same thing.", - set_1.equals(set_2)); + Objects.equals(set_1, set_2)); set_1 = properties.getIntCharSet(" Script:Tibetan "); assertNotNull("Null interval set returned for \\p{ Script:Tibetan }", set_1); @@ -103,9 +103,9 @@ public void testDefaultVersionAliases() { assertTrue("Empty interval set returned for \\p{-_T i b t_-}", set_1.containsElements()); assertTrue( "\\p{ Script:Tibetan } and \\p{-_T i b t_-} should" + " return the same thing.", - set_1.equals(set_2)); + Objects.equals(set_1, set_2)); } catch (UnicodeProperties.UnsupportedUnicodeVersionException e) { - assertTrue("Default version is unsupported: " + e, false); + fail("Default version is unsupported: " + e); } } @@ -126,7 +126,7 @@ public void testCaselessMatches_1_1() { + caselessMatches.numIntervals(), caselessMatches.numIntervals() == 2); } catch (UnicodeProperties.UnsupportedUnicodeVersionException e) { - assertTrue("Unsupported version '1.1' should be supported: " + e, false); + fail("Unsupported version '1.1' should be supported: " + e); } } @@ -135,7 +135,7 @@ public void testCaselessMatches_2_0() { UnicodeProperties properties = new UnicodeProperties("2.0"); checkCaseless_i_matches(properties); } catch (UnicodeProperties.UnsupportedUnicodeVersionException e) { - assertTrue("Unsupported version '2.0' should be supported: " + e, false); + fail("Unsupported version '2.0' should be supported: " + e); } } @@ -173,7 +173,7 @@ public void testCaselessMatches_2_1() { UnicodeProperties properties = new UnicodeProperties("2.1"); checkCaseless_i_matches(properties); } catch (UnicodeProperties.UnsupportedUnicodeVersionException e) { - assertTrue("Unsupported version '2.1' should be supported: " + e, false); + fail("Unsupported version '2.1' should be supported: " + e); } } @@ -182,7 +182,7 @@ public void testCaselessMatches_3_0() { UnicodeProperties properties = new UnicodeProperties("3.0"); checkCaseless_i_matches(properties); } catch (UnicodeProperties.UnsupportedUnicodeVersionException e) { - assertTrue("Unsupported version '3.0' should be supported: " + e, false); + fail("Unsupported version '3.0' should be supported: " + e); } } @@ -191,7 +191,7 @@ public void testCaselessMatches_3_1() { UnicodeProperties properties = new UnicodeProperties("3.1"); checkCaseless_i_matches(properties); } catch (UnicodeProperties.UnsupportedUnicodeVersionException e) { - assertTrue("Unsupported version '3.1' should be supported: " + e, false); + fail("Unsupported version '3.1' should be supported: " + e); } } @@ -200,7 +200,7 @@ public void testCaselessMatches_3_2() { UnicodeProperties properties = new UnicodeProperties("3.2"); checkCaseless_i_matches(properties); } catch (UnicodeProperties.UnsupportedUnicodeVersionException e) { - assertTrue("Unsupported version '3.2' should be supported: " + e, false); + fail("Unsupported version '3.2' should be supported: " + e); } } @@ -209,7 +209,7 @@ public void testCaselessMatches_4_0() { UnicodeProperties properties = new UnicodeProperties("4.0"); checkCaseless_i_matches(properties); } catch (UnicodeProperties.UnsupportedUnicodeVersionException e) { - assertTrue("Unsupported version '4.0' should be supported: " + e, false); + fail("Unsupported version '4.0' should be supported: " + e); } } @@ -218,7 +218,7 @@ public void testCaselessMatches_4_1() { UnicodeProperties properties = new UnicodeProperties("4.1"); checkCaseless_i_matches(properties); } catch (UnicodeProperties.UnsupportedUnicodeVersionException e) { - assertTrue("Unsupported version '4.1' should be supported: " + e, false); + fail("Unsupported version '4.1' should be supported: " + e); } } @@ -227,7 +227,7 @@ public void testCaselessMatches_5_0() { UnicodeProperties properties = new UnicodeProperties("5.0"); checkCaseless_i_matches(properties); } catch (UnicodeProperties.UnsupportedUnicodeVersionException e) { - assertTrue("Unsupported version '5.0' should be supported: " + e, false); + fail("Unsupported version '5.0' should be supported: " + e); } } @@ -241,7 +241,7 @@ public void testSingleLetterProperties_5_0() { assertNotNull("Null interval set for \\p{Symbol}", set_2); assertTrue("Empty interval set for \\p{Symbol}", set_2.containsElements()); - assertTrue("\\p{S} is not the same as \\p{Symbol}", set_1.equals(set_2)); + assertTrue("\\p{S} is not the same as \\p{Symbol}", Objects.equals(set_1, set_2)); // 0024;DOLLAR SIGN;Sc;0;ET;;;;;N;;;;; assertTrue("\\p{S} does not contain \\u0024 '\u0024' (\\p{Sc})", set_1.contains('\u0024')); @@ -255,7 +255,7 @@ public void testSingleLetterProperties_5_0() { assertTrue("\\p{S} does not contain \\uFF04 (\\p{Sc}", set_1.contains('\uFF04')); } catch (UnicodeProperties.UnsupportedUnicodeVersionException e) { - assertTrue("Version '5.0' not supported: " + e, false); + fail("Version '5.0' not supported: " + e); } } @@ -264,7 +264,7 @@ public void testCaselessMatches_5_1() { UnicodeProperties properties = new UnicodeProperties("5.1"); checkCaseless_i_matches(properties); } catch (UnicodeProperties.UnsupportedUnicodeVersionException e) { - assertTrue("Unsupported version '5.1' should be supported: " + e, false); + fail("Unsupported version '5.1' should be supported: " + e); } } @@ -278,7 +278,7 @@ public void testSingleLetterProperties_5_1() { assertNotNull("Null interval set for \\p{Symbol}", set_2); assertTrue("Empty interval set for \\p{Symbol}", set_2.containsElements()); - assertTrue("\\p{S} is not the same as \\p{Symbol}", set_1.equals(set_2)); + assertTrue("\\p{S} is not the same as \\p{Symbol}", Objects.equals(set_1, set_2)); // 0024;DOLLAR SIGN;Sc;0;ET;;;;;N;;;;; assertTrue("\\p{S} does not contain \\u0024 '\u0024' (\\p{Sc})", set_1.contains('\u0024')); @@ -292,7 +292,7 @@ public void testSingleLetterProperties_5_1() { assertTrue("\\p{S} does not contain \\uFF04 (\\p{Sc}", set_1.contains('\uFF04')); } catch (UnicodeProperties.UnsupportedUnicodeVersionException e) { - assertTrue("Version '5.1' not supported: " + e, false); + fail("Version '5.1' not supported: " + e); } } @@ -301,7 +301,7 @@ public void testCaselessMatches_5_2() { UnicodeProperties properties = new UnicodeProperties("5.2"); checkCaseless_i_matches(properties); } catch (UnicodeProperties.UnsupportedUnicodeVersionException e) { - assertTrue("Unsupported version '5.2' should be supported: " + e, false); + fail("Unsupported version '5.2' should be supported: " + e); } } @@ -315,7 +315,7 @@ public void testSingleLetterProperties_5_2() { assertNotNull("Null interval set for \\p{Symbol}", set_2); assertTrue("Empty interval set for \\p{Symbol}", set_2.containsElements()); - assertTrue("\\p{S} is not the same as \\p{Symbol}", set_1.equals(set_2)); + assertTrue("\\p{S} is not the same as \\p{Symbol}", Objects.equals(set_1, set_2)); // 0024;DOLLAR SIGN;Sc;0;ET;;;;;N;;;;; assertTrue("\\p{S} does not contain \\u0024 '\u0024' (\\p{Sc})", set_1.contains('\u0024')); @@ -329,7 +329,7 @@ public void testSingleLetterProperties_5_2() { assertTrue("\\p{S} does not contain \\uFF04 (\\p{Sc}", set_1.contains('\uFF04')); } catch (UnicodeProperties.UnsupportedUnicodeVersionException e) { - assertTrue("Version '5.2' not supported: " + e, false); + fail("Version '5.2' not supported: " + e); } } @@ -338,7 +338,7 @@ public void testCaselessMatches_6_0() { UnicodeProperties properties = new UnicodeProperties("6.0"); checkCaseless_i_matches(properties); } catch (UnicodeProperties.UnsupportedUnicodeVersionException e) { - assertTrue("Unsupported version '6.0' should be supported: " + e, false); + fail("Unsupported version '6.0' should be supported: " + e); } } @@ -352,7 +352,7 @@ public void testSingleLetterProperties_6_0() { assertNotNull("Null interval set for \\p{Symbol}", set_2); assertTrue("Empty interval set for \\p{Symbol}", set_2.containsElements()); - assertTrue("\\p{S} is not the same as \\p{Symbol}", set_1.equals(set_2)); + assertTrue("\\p{S} is not the same as \\p{Symbol}", Objects.equals(set_1, set_2)); // 0024;DOLLAR SIGN;Sc;0;ET;;;;;N;;;;; assertTrue("\\p{S} does not contain \\u0024 '\u0024' (\\p{Sc})", set_1.contains('\u0024')); @@ -366,7 +366,7 @@ public void testSingleLetterProperties_6_0() { assertTrue("\\p{S} does not contain \\uFF04 (\\p{Sc}", set_1.contains('\uFF04')); } catch (UnicodeProperties.UnsupportedUnicodeVersionException e) { - assertTrue("Version '6.0' not supported: " + e, false); + fail("Version '6.0' not supported: " + e); } } } diff --git a/jflex/src/test/java/jflex/io/FileUtilTest.java b/jflex/src/test/java/jflex/io/FileUtilTest.java new file mode 100644 index 000000000..f6bfb1bff --- /dev/null +++ b/jflex/src/test/java/jflex/io/FileUtilTest.java @@ -0,0 +1,36 @@ +package jflex.io; + +import static org.junit.Assert.*; + +import java.io.File; +import junit.framework.TestCase; +import org.junit.Test; + +public class FileUtilTest extends TestCase { + + @Test + public void test_getRelativePath_fileInDir() { + File dir = new File("/a/b/c"); + File f = new File("/a/b/c/d/foo.bar"); + assertEquals("d/foo.bar", FileUtil.getRelativePath(dir, f)); + } + + @Test + public void test_getRelativePath_fileNotInDir() { + File dir = new File("/a/b/c"); + File f = new File("/d/e/f/foo.bar"); + assertEquals("/d/e/f/foo.bar", FileUtil.getRelativePath(dir, f)); + } + + @Test + public void test_getRelativePath_sameStart() { + File dir = new File("/a/b/c"); + File f = new File("/a/b/c.txt"); + assertEquals("/a/b/c.txt", FileUtil.getRelativePath(dir, f)); + } + + @Test + public void test_slashify() { + assertEquals("C:/u0022.txt", FileUtil.slashify("C:\\u0022.txt")); + } +} diff --git a/mvn-deploy.sh b/mvn-deploy.sh index 9bbd1e9c8..1379c83f4 100755 --- a/mvn-deploy.sh +++ b/mvn-deploy.sh @@ -21,7 +21,7 @@ set -e # Abort if the checkout is not clean printf "Clean checkout? " -stat_results=`git status -s` +stat_results=$(git status -s) if [ ! -z "$stat_results" ] ; then printf "NO!\n\n${stat_results}\nAborting.\n" exit 1 diff --git a/mvnw b/mvnw index e96ccd5fb..5551fde8e 100755 --- a/mvnw +++ b/mvnw @@ -200,6 +200,65 @@ if [ -z "$BASE_DIR" ]; then exit 1; fi +########################################################################################## +# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +# This allows using the maven wrapper in projects that prohibit checking in binary data. +########################################################################################## +if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found .mvn/wrapper/maven-wrapper.jar" + fi +else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." + fi + jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar" + while IFS="=" read key value; do + case "$key" in (wrapperUrl) jarUrl="$value"; break ;; + esac + done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" + if [ "$MVNW_VERBOSE" = true ]; then + echo "Downloading from: $jarUrl" + fi + wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" + + if command -v wget > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found wget ... using wget" + fi + wget "$jarUrl" -O "$wrapperJarPath" + elif command -v curl > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found curl ... using curl" + fi + curl -o "$wrapperJarPath" "$jarUrl" + else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Falling back to using Java to download" + fi + javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" + if [ -e "$javaClass" ]; then + if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Compiling MavenWrapperDownloader.java ..." + fi + # Compiling the Java class + ("$JAVA_HOME/bin/javac" "$javaClass") + fi + if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + # Running the downloader + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Running MavenWrapperDownloader.java ..." + fi + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} if [ "$MVNW_VERBOSE" = true ]; then echo $MAVEN_PROJECTBASEDIR diff --git a/mvnw.cmd b/mvnw.cmd index 6a6eec39b..48363fa60 100755 --- a/mvnw.cmd +++ b/mvnw.cmd @@ -1,145 +1,161 @@ -@REM ---------------------------------------------------------------------------- -@REM Licensed to the Apache Software Foundation (ASF) under one -@REM or more contributor license agreements. See the NOTICE file -@REM distributed with this work for additional information -@REM regarding copyright ownership. The ASF licenses this file -@REM to you under the Apache License, Version 2.0 (the -@REM "License"); you may not use this file except in compliance -@REM with the License. You may obtain a copy of the License at -@REM -@REM http://www.apache.org/licenses/LICENSE-2.0 -@REM -@REM Unless required by applicable law or agreed to in writing, -@REM software distributed under the License is distributed on an -@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -@REM KIND, either express or implied. See the License for the -@REM specific language governing permissions and limitations -@REM under the License. -@REM ---------------------------------------------------------------------------- - -@REM ---------------------------------------------------------------------------- -@REM Maven2 Start Up Batch script -@REM -@REM Required ENV vars: -@REM JAVA_HOME - location of a JDK home dir -@REM -@REM Optional ENV vars -@REM M2_HOME - location of maven2's installed home dir -@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands -@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending -@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven -@REM e.g. to debug Maven itself, use -@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 -@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files -@REM ---------------------------------------------------------------------------- - -@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' -@echo off -@REM set title of command window -title %0 -@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' -@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% - -@REM set %HOME% to equivalent of $HOME -if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") - -@REM Execute a user defined script before this one -if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre -@REM check for pre script, once with legacy .bat ending and once with .cmd ending -if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" -if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" -:skipRcPre - -@setlocal - -set ERROR_CODE=0 - -@REM To isolate internal variables from possible post scripts, we use another setlocal -@setlocal - -@REM ==== START VALIDATION ==== -if not "%JAVA_HOME%" == "" goto OkJHome - -echo. -echo Error: JAVA_HOME not found in your environment. >&2 -echo Please set the JAVA_HOME variable in your environment to match the >&2 -echo location of your Java installation. >&2 -echo. -goto error - -:OkJHome -if exist "%JAVA_HOME%\bin\java.exe" goto init - -echo. -echo Error: JAVA_HOME is set to an invalid directory. >&2 -echo JAVA_HOME = "%JAVA_HOME%" >&2 -echo Please set the JAVA_HOME variable in your environment to match the >&2 -echo location of your Java installation. >&2 -echo. -goto error - -@REM ==== END VALIDATION ==== - -:init - -@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". -@REM Fallback to current working directory if not found. - -set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% -IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir - -set EXEC_DIR=%CD% -set WDIR=%EXEC_DIR% -:findBaseDir -IF EXIST "%WDIR%"\.mvn goto baseDirFound -cd .. -IF "%WDIR%"=="%CD%" goto baseDirNotFound -set WDIR=%CD% -goto findBaseDir - -:baseDirFound -set MAVEN_PROJECTBASEDIR=%WDIR% -cd "%EXEC_DIR%" -goto endDetectBaseDir - -:baseDirNotFound -set MAVEN_PROJECTBASEDIR=%EXEC_DIR% -cd "%EXEC_DIR%" - -:endDetectBaseDir - -IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig - -@setlocal EnableExtensions EnableDelayedExpansion -for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a -@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% - -:endReadAdditionalConfig - -SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" - -set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" -set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain - -%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* -if ERRORLEVEL 1 goto error -goto end - -:error -set ERROR_CODE=1 - -:end -@endlocal & set ERROR_CODE=%ERROR_CODE% - -if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost -@REM check for post script, once with legacy .bat ending and once with .cmd ending -if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" -if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" -:skipRcPost - -@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' -if "%MAVEN_BATCH_PAUSE%" == "on" pause - -if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% - -exit /B %ERROR_CODE% +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven2 Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" +if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar" +FOR /F "tokens=1,2 delims==" %%A IN (%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties) DO ( + IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + echo Found %WRAPPER_JAR% +) else ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %DOWNLOAD_URL% + powershell -Command "(New-Object Net.WebClient).DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')" + echo Finished downloading %WRAPPER_JAR% +) +@REM End of extension + +%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" +if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%" == "on" pause + +if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% + +exit /B %ERROR_CODE% diff --git a/pom.xml b/pom.xml index 86cda27f0..1568fb9ed 100644 --- a/pom.xml +++ b/pom.xml @@ -54,6 +54,7 @@ jflex jflex-maven-plugin testsuite + report-module @@ -103,15 +104,21 @@ + + com.google.code.findbugs + jsr305 + 3.0.2 + com.google.guava guava + 20.0 com.google.truth truth - 0.36 + 0.42 commons-io @@ -131,7 +138,7 @@ org.apache.ant ant - 1.9.7 + 1.10.5 org.apache.maven @@ -151,7 +158,7 @@ org.apache.maven.plugin-tools maven-plugin-annotations - 3.5 + 3.5.2 org.apache.maven.plugin-testing @@ -166,7 +173,7 @@ org.mockito mockito-core - 2.11.0 + 2.23.0 test @@ -175,6 +182,11 @@ + + org.jacoco + jacoco-maven-plugin + 0.8.2 + de.jflex cup-maven-plugin @@ -183,7 +195,7 @@ com.coveo fmt-maven-plugin - 2.2.0 + 2.5.0 maven-compiler-plugin @@ -274,16 +286,18 @@ maven-surefire-plugin 2.20.1 - - org.codehaus.mojo - findbugs-maven-plugin - 3.0.5 - + + maven-compiler-plugin + + 1.7 + 1.7 + + maven-site-plugin @@ -302,6 +316,24 @@ true + + org.jacoco + jacoco-maven-plugin + + + + prepare-agent + + + + generate-code-coverage-report + test + + report + + + + @@ -316,8 +348,6 @@ index summary plugins - dependencies - plugins @@ -329,20 +359,6 @@ true - - org.codehaus.mojo - findbugs-maven-plugin - - true - Normal - Default - target/findbugs - FindDeadLocalStores,UnreadFields - de.jflex.* - true - true - - @@ -431,8 +447,8 @@ javac-with-errorprone true - 7 - 7 + 1.7 + 1.7 diff --git a/report-module/README.md b/report-module/README.md new file mode 100644 index 000000000..679e79822 --- /dev/null +++ b/report-module/README.md @@ -0,0 +1,12 @@ +# Maven reports + +This module aggregates reports from other modules. + +The reason for this module is that: + +- the Maven parent is executed first +- the reports are created in every module +- Maven doesn't run the reporting again on the parent. + +In particular, this is used to aggregated code coverage of java tests. + diff --git a/report-module/pom.xml b/report-module/pom.xml new file mode 100644 index 000000000..8e875516d --- /dev/null +++ b/report-module/pom.xml @@ -0,0 +1,95 @@ + + + 4.0.0 + + de.jflex + jflex-parent + 1.7.1-SNAPSHOT + ../pom.xml + + report-module + jar + Report aggregator for JFlex Modules + + Aggregates code quality reports for JFlex. + The reason it's a separate maven module is because the parent is built first, + hence its aggregated reports. + Ideally, maven would run a second pass on the parent when it's also an aggregator, + but that's not what maven does. + + 2018 + + + regisd + Régis Décamps + + developer + + +1 + + + + + benjamin.bentmann@udo.edu + Benjamin Bentmann + + reviewer + + + + support_0384@newplanetsoftware.com + John Lindal + + reviewer + + + + + + de.jflex + jflex + ${project.version} + + + de.jflex + jflex-maven-plugin + ${project.version} + + + de.jflex + cup-maven-plugin + 1.0 + + + de.jflex + jflex-unicode-maven-plugin + ${project.version} + + + + + + + org.jacoco + jacoco-maven-plugin + + + jacoco-initialize + + prepare-agent + + + + report + test + + report-aggregate + + + + + + + diff --git a/scripts/README.md b/scripts/README.md new file mode 100644 index 000000000..0d2feb9e2 --- /dev/null +++ b/scripts/README.md @@ -0,0 +1,4 @@ +# CI scripts + +Various scripts used by the Continuous Integration; and Travis in particular. + diff --git a/scripts/ant-build.sh b/scripts/ant-build.sh deleted file mode 100755 index 7722ea2e6..000000000 --- a/scripts/ant-build.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/bash -# Build with ant - -CWD="$PWD" -BASEDIR="$(cd "$(dirname "$0")" && pwd -P)"/.. -# Provides the logi function -source "$BASEDIR"/scripts/logger.sh -# fail on error -set -e - -logi "Compile with ant" -cd "$BASEDIR"/jflex; ant gettools build test; cd .. - -cd "$CWD" diff --git a/scripts/bazel.sh b/scripts/bazel.sh new file mode 100755 index 000000000..eeade1798 --- /dev/null +++ b/scripts/bazel.sh @@ -0,0 +1,38 @@ +#!/bin/bash +# Run bazel on examples + +CWD="$PWD" +BASEDIR="$(cd "$(dirname "$0")" && pwd -P)"/.. +# Provides the logi function +source "$BASEDIR"/scripts/logger.sh +# fail on error +set -e + +if [[ $TRAVIS ]]; then + BAZEL="bazel --bazelrc=$TRAVIS_BUILD_DIR/.travis.bazelrc --output_user_root=${HOME}/__bazel_travis_root__ --output_base=${HOME}/__bazel_output_base__" +else + BAZEL='bazel' +fi + +logi "Start Bazel" +logi "===========" +$BAZEL info + +logi "Build everything" +logi "================" +$BAZEL build //... + +logi "Test everything" +logi "===============" +$BAZEL test //... + + +if [[ -n "$CI" ]]; then + logi "Shutdown" + logi "========" + $BAZEL shutdown +else + logi "Done" +fi +logi "Done ✅" +cd "$CWD" diff --git a/scripts/deploy-source-code.sh b/scripts/deploy-source-code.sh index 1f65dabea..7cfee0a7f 100755 --- a/scripts/deploy-source-code.sh +++ b/scripts/deploy-source-code.sh @@ -35,9 +35,11 @@ update_source() { logi "Download deps and Compile" ./compile.sh - logi "Add license files" - cp ../LICENSE.md . - cp ../cup/LICENSE LICENSE_CUP + logi "Checking licenses" + [[ -f LICENSE_CUP ]] || loge "Missing LICENSE_CUP for CUP" + [[ -f LICENSE_JFLEX ]] || loge "Missing LICENSE_JFLEX for JFlex" + [[ $(head -1 LICENSE_JFLEX | cut -f 1 -d " ") == "JFlex" ]] || loge "JFlex license has bad content" + logi "Update git sources" git add --all @@ -45,7 +47,10 @@ update_source() { git status # Don't commit if the diff is empty. # git commit fails if the commit is empty, which makes Travis build fail. - git diff-index --quiet HEAD || git commit -a -m "Update from $version\n\nInitial $gitlog" + git diff-index --quiet HEAD || \ + git commit -a \ + -m "Update from $version" \ + -m "Initial $gitlog" cd .. } @@ -72,3 +77,5 @@ if [[ -z "$CI" ]]; then else git_push fi + +cd "$CWD" diff --git a/scripts/logger.sh b/scripts/logger.sh index 7ba154902..5ac52e41c 100755 --- a/scripts/logger.sh +++ b/scripts/logger.sh @@ -15,3 +15,11 @@ function logi { echo $* fi } +function loge { + if [[ $COLOR_CAPABILITY ]]; then + printf "${RED}${*}${NC}\n" + else + echo "[ERROR] ${*}" + fi + exit 1 +} diff --git a/scripts/mk-release.sh b/scripts/mk-release.sh index 77021a82d..9e299067f 100755 --- a/scripts/mk-release.sh +++ b/scripts/mk-release.sh @@ -3,9 +3,6 @@ # fail on error set -e -# a hack for enabling ant builds outside the repo until we figure out something better -CUP_URL="https://raw.githubusercontent.com/jflex-de/jflex/05632859c94c348dee7d243e4a36bd656c132e96/cup/cup/java-cup-11b.jar" - VERSION="1.7.1-SNAPSHOT" JFLEX_JAR="jflex-full-$VERSION.jar" @@ -16,7 +13,7 @@ GPG=gpg KEY=84A70085 printf "Clean checkout? " -stat_results=`git status -s` +stat_results=$(git status -s) if [ ! -z "$stat_results" ] ; then printf "NO!\n\n${stat_results}\nAborting.\n" exit 1 @@ -44,9 +41,6 @@ echo "------[ Packaging jflex ]" cp ../../pom.xml parent.xml perl -pi -e "s|../pom.xml|parent.xml|" pom.xml -# hack for ant build outside repo -perl -pi -e "s|||" build.xml - # build things "$MVN" package diff --git a/scripts/mvn-install-fastbuild.sh b/scripts/mvn-install-fastbuild.sh new file mode 100755 index 000000000..ec05f41de --- /dev/null +++ b/scripts/mvn-install-fastbuild.sh @@ -0,0 +1,26 @@ +#!/bin/bash + +EXTRA_PROJECTS=$1 + +BASEDIR="$(cd "$(dirname "$0")" && pwd -P)"/.. +# Provides the logi function +source "$BASEDIR"/scripts/logger.sh +MVN="$BASEDIR"/mvnw + +QUIET="" +if [[ "$TRAVIS" ]]; then + # Quiet mode shows errors only. + QUIET="--quiet" +fi + +# TODO(regisd) Define the list of packages via the profile +logi "Build and install JFlex only (-P fastbuild)" +cd "$BASEDIR" +"$MVN" ${QUIET} -Pfastbuild -pl .,cup,cup/cup,cup/cup_runtime,cup-maven-plugin,jflex install + +if [[ -n "$EXTRA_PROJECTS" ]]; then + "$MVN" ${QUIET} -Pfastbuild -pl "$EXTRA_PROJECTS" install +fi + +cd "$CWD" + diff --git a/scripts/run-tests.sh b/scripts/run-tests.sh index fe45e0723..d1ae4051d 100755 --- a/scripts/run-tests.sh +++ b/scripts/run-tests.sh @@ -4,22 +4,21 @@ CWD="$PWD" BASEDIR="$(cd "$(dirname "$0")" && pwd -P)"/.. # Provides the logi function source "$BASEDIR"/scripts/logger.sh -# Maven executable -MVN="$BASEDIR"/mvnw # fail on error set -e -echo '============================== JAVA VERSION ==============================' -java -version -javac -version -echo '============================================================================' +if [[ $TRAVIS ]]; then + loge "This script is only for manual invocation" + exit 1 +fi # Clean environment -if [[ ! $TRAVIS ]]; then - "$BASEDIR"/scripts/clean.sh -fi +"$BASEDIR"/scripts/clean.sh # Travis then runs _in parallel_ (but we do it in sequence) +if [[ -z "$TEST_SUITE" || "$TEST_SUITE" == "java-format" ]]; then + "$BASEDIR"/scripts/test-java-format.sh +fi if [[ -z "$TEST_SUITE" || "$TEST_SUITE" == "unit" ]]; then "$BASEDIR"/scripts/test-unit.sh fi @@ -28,6 +27,10 @@ if [[ -z "$TEST_SUITE" || "$TEST_SUITE" == "regression" ]]; then fi if [[ -z "$TEST_SUITE" || "$TEST_SUITE" == "ant" ]]; then "$BASEDIR"/scripts/ant-build.sh + "$BASEDIR"/scripts/test-examples.sh +fi +if [[ -z "$TEST_SUITE" || "$TEST_SUITE" == "bazel" ]]; then + "$BASEDIR"/scripts/bazel.sh fi logi "Success" diff --git a/scripts/send-code-coverage.sh b/scripts/send-code-coverage.sh new file mode 100755 index 000000000..cb17ca4d9 --- /dev/null +++ b/scripts/send-code-coverage.sh @@ -0,0 +1,15 @@ +#!/bin/bash +mkdir -p tools + +if [[ -z "${CODACY_PROJECT_TOKEN}" ]]; then + echo "Missing \$CODACY_PROJECT_TOKEN" + exit 1 +fi +if [[ "${TRAVIS_PULL_REQUEST_SLUG}:${TRAVIS_BRANCH}" = ":master" ]]; then + # https://app.codacy.com/project/regisd/jflex/dashboard + curl -C - -L https://github.com/codacy/codacy-coverage-reporter/releases/download/4.0.3/codacy-coverage-reporter-4.0.3-assembly.jar -o tools/codacy-coverage-reporter.jar + echo "Sending code coverage report fo Codacy" + java -jar tools/codacy-coverage-reporter.jar report -l Java -r report-module/target/site/jacoco-aggregate/jacoco.xml +else + echo "Only Travis merging in main branch sends code coverage reports" +fi diff --git a/scripts/test-examples.sh b/scripts/test-examples.sh new file mode 100755 index 000000000..f4260b042 --- /dev/null +++ b/scripts/test-examples.sh @@ -0,0 +1,74 @@ +#!/bin/bash +# Run unit tests + +CWD="$PWD" +BASEDIR="$(cd "$(dirname "$0")" && pwd -P)"/.. +# Provides the logi function +source "$BASEDIR"/scripts/logger.sh +# Maven executable +MVN="$BASEDIR"/mvnw +# Exit with error in case of error (see #242) +set -e + +logi "Compile, test and install all" +logi "=============================" +"$BASEDIR"/scripts/mvn-install-fastbuild.sh jflex-maven-plugin + +logi "Run jflex examples with ant" +logi "===========================" +# Some tests invoke /bin/jflex which expects the jar in /lib +cp "$BASEDIR"/jflex/target/jflex-full-*.jar "$BASEDIR"/jflex/lib + +cd "$BASEDIR"/jflex/examples + +logi "Example: byaccj" +cd byaccj +# Maven not supported +# ant not supported +# don't assume byacc/j is installed, just run lexer +make Yylex.java +cd .. + +logi "Example: cup-interpreter" +cd cup-interpreter +"$MVN" test +ant test +make test +cd .. + +logi "Example: cup-java" +cd cup-java +"$MVN" test +ant test +make test +cd .. + +logi "Example: cup-lcalc" +cd cup-lcalc +"$MVN" test +# ant test +# make test +cd .. + +logi "Example: simple" +cd simple +"$MVN" test +ant test +# make test +cd .. + +logi "Example: standalone" +cd standalone +"$MVN" test +# ant test +# make test +cd .. + +logi "Example: zero-reader" +cd zero-reader +"$MVN" test +# ant test +make test +cd .. + +cd "$CWD" diff --git a/scripts/test-java-format.sh b/scripts/test-java-format.sh new file mode 100755 index 000000000..4a7d02564 --- /dev/null +++ b/scripts/test-java-format.sh @@ -0,0 +1,36 @@ +#!/bin/bash +# Run the java-format code style +TOOLSDIR=buildtools +CWD="$PWD" +BASEDIR="$(cd "$(dirname "$0")" && pwd -P)"/.. +# Provides the logi function +source "$BASEDIR"/scripts/logger.sh +# fail on error +set -e + +if [[ "_${TRAVIS_JDK_VERSION}" = "_openjdk7" ]]; then + logi "Skip google-java-format. Unsupported java version." + exit +fi + +function gjf() { + directory=$1 + logi "Checking $directory" + java -jar $TOOLSDIR/google-java-format.jar --dry-run --set-exit-if-changed $(find $directory -name '*.java') +} + +logi "Download google-java-format" +logi "===========================" +echo "TRAVIS_JDK_VERSION=$TRAVIS_JDK_VERSION" +mkdir -p $TOOLSDIR +curl -C - -L https://github.com/google/google-java-format/releases/download/google-java-format-1.6/google-java-format-1.6-all-deps.jar -o $TOOLSDIR/google-java-format.jar + +logi "Check java format" +logi "=================" +java -jar $TOOLSDIR/google-java-format.jar --version +gjf cup-maven-plugin +gjf jflex +gjf jflex-maven-plugin +gjf jflex-unicode-maven-plugin +gjf testsuite/jflex-testsuite-maven-plugin +cd "$CWD" diff --git a/scripts/test-regression.sh b/scripts/test-regression.sh index a94d64b7c..abb6d86a6 100755 --- a/scripts/test-regression.sh +++ b/scripts/test-regression.sh @@ -10,17 +10,8 @@ MVN="$BASEDIR"/mvnw # fail on error set -e -if [[ $TRAVIS ]]; then - logi "Compile and install all (no tests)" - # Travis has "`install: true` and hence jflex needs to be install in local repo - "$MVN" install -Pfastbuild --quiet -else - # On local dev, ./run-tests has already installed all, so we can skip installation - echo -fi - - logi "Run regression test cases" +logi "=========================" # regression test suite must run in its own directory cd "$BASEDIR"/testsuite/testcases; "$MVN" test cd "$CWD" diff --git a/scripts/test-unit.sh b/scripts/test-unit.sh index 2fe3377af..cf75d109b 100755 --- a/scripts/test-unit.sh +++ b/scripts/test-unit.sh @@ -14,38 +14,11 @@ logi "Compile, test and install all" logi "=============================" # NB: Installs jflex in local repo # implies: validate, compile, test, package, verify, install -if [[ $TRAVIS ]]; then +if [[ "$TRAVIS" ]]; then # Quiet mode shows errors only. "$MVN" install --quiet else "$MVN" install fi -logi "Run jflex examples" -logi "==================" -# Some tests invoke /bin/jflex which expects the jar in /lib -cp "$BASEDIR"/jflex/target/jflex-full-*.jar "$BASEDIR"/jflex/lib -# Exit with error in case of error (see #242) -set -x - -cd "$BASEDIR"/jflex/examples -logi "Example: simple-maven" -cd simple-maven; $MVN test; cd .. -logi "Example: standalone-maven" -# Note that mvn is a likely to be a different and older version of Maven. -cd standalone-maven; $MVN test; cd .. -logi "Example: cup-maven" -cd cup-maven; $MVN test; cd .. - -logi "Example: byacc/j" -# don't assume byacc/j is installed, just run lexer -cd byaccj; make clean; make Yylex.java; cd .. -logi "Example: interpreter" -cd interpreter; make clean; make; cd .. -logi "Example: java" -cd java; make clean; make; cd .. -logi "Example: zero-reader" -cd zero-reader; make clean; make; cd .. -set +x - cd "$CWD" diff --git a/src/README.md b/src/README.md new file mode 100644 index 000000000..917fc0fa5 --- /dev/null +++ b/src/README.md @@ -0,0 +1,9 @@ +# JFlex parent sources + +Sources for the JFlex parent. + +Mostly contains shared reosources, such as + +- Code Checkstyle configuration +- PMD configuration +- Maven site configuration diff --git a/src/main/resources/google_checks.xml b/src/main/resources/google_checks.xml new file mode 100644 index 000000000..2ad67870f --- /dev/null +++ b/src/main/resources/google_checks.xml @@ -0,0 +1,255 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/site/site.xml b/src/site/site.xml index eb0aa8e34..feadf6414 100644 --- a/src/site/site.xml +++ b/src/site/site.xml @@ -11,11 +11,6 @@

- - - - - diff --git a/testsuite/README.md b/testsuite/README.md new file mode 100644 index 000000000..d37772202 --- /dev/null +++ b/testsuite/README.md @@ -0,0 +1,13 @@ +# JFlex test suite + +This directory contains a conformance test suite for JFlex. + +## jflex-testsuite-maven-plugin + +A Maven plugin to exectute test cases. + +## testcases + +A simple Maven project using the jflex-testsuite-maven-plugin +and actually containing the test cases. + diff --git a/testsuite/bzltestsuite/BUILD b/testsuite/bzltestsuite/BUILD new file mode 100644 index 000000000..f7cf27474 --- /dev/null +++ b/testsuite/bzltestsuite/BUILD @@ -0,0 +1,6 @@ +package(default_visibility = ["//testsuite:__subpackages__"]) + +alias( + name = "bzltestsuite", + actual = "//testsuite/bzltestsuite/java/jflex/testing", +) diff --git a/testsuite/bzltestsuite/README.md b/testsuite/bzltestsuite/README.md new file mode 100644 index 000000000..42adb6bda --- /dev/null +++ b/testsuite/bzltestsuite/README.md @@ -0,0 +1,4 @@ +# Test suite with Bazel [WIP] + +Attempt to rewrite the test suite with Bazel. + diff --git a/testsuite/bzltestsuite/java/jflex/testing/BUILD b/testsuite/bzltestsuite/java/jflex/testing/BUILD new file mode 100644 index 000000000..ef51e0729 --- /dev/null +++ b/testsuite/bzltestsuite/java/jflex/testing/BUILD @@ -0,0 +1,13 @@ +package(default_visibility = ["//testsuite:__subpackages__"]) + +java_library( + name = "testing", + srcs = glob(["*.java"]), + deps = [ + "//jflex", + "//testsuite/bzltestsuite/java/jflex/testing/annotations", + "//testsuite/bzltestsuite/java/jflex/testing/javac", + "//third_party/com/google/guava", + "//third_party/junit", + ], +) diff --git a/testsuite/bzltestsuite/java/jflex/testing/JFlexTestRunner.java b/testsuite/bzltestsuite/java/jflex/testing/JFlexTestRunner.java new file mode 100644 index 000000000..2ab30e08a --- /dev/null +++ b/testsuite/bzltestsuite/java/jflex/testing/JFlexTestRunner.java @@ -0,0 +1,56 @@ +package jflex.testing; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.common.collect.ImmutableList; +import java.io.File; +import jflex.LexGenerator; +import jflex.testing.annotations.TestSpec; +import jflex.testing.javac.CompilerException; +import jflex.testing.javac.JavacUtil; +import org.junit.runner.Description; +import org.junit.runner.notification.Failure; +import org.junit.runner.notification.RunNotifier; +import org.junit.runners.BlockJUnit4ClassRunner; +import org.junit.runners.model.InitializationError; + +public class JFlexTestRunner extends BlockJUnit4ClassRunner { + + private final Class klass; + private final TestSpec spec; + + public JFlexTestRunner(Class testClass) throws InitializationError { + super(testClass); + this.klass = testClass; + this.spec = checkNotNull(testClass.getAnnotation(TestSpec.class)); + } + + @Override + public Description getDescription() { + return Description.createTestDescription(klass, "JFlex test case"); + } + + @Override + public void run(RunNotifier notifier) { + String lexerJavaFileName = generateLexer(notifier); + try { + compile(notifier, ImmutableList.of(lexerJavaFileName)); + } catch (CompilerException e) { + notifier.fireTestFailure( + new Failure(Description.createTestDescription(klass, "Failed to compile java code"), e)); + } + super.run(notifier); + } + + private String generateLexer(RunNotifier notifier) { + notifier.fireTestStarted(Description.createTestDescription(klass, "Generate Lexer")); + String lexerJavaFileName = LexGenerator.generate(new File(spec.lex())); + return lexerJavaFileName; + } + + private void compile(RunNotifier notifier, ImmutableList javaFileNames) + throws CompilerException { + notifier.fireTestStarted(Description.createTestDescription(klass, "Compile java code")); + JavacUtil.compile(javaFileNames); + } +} diff --git a/testsuite/bzltestsuite/java/jflex/testing/annotations/BUILD b/testsuite/bzltestsuite/java/jflex/testing/annotations/BUILD new file mode 100644 index 000000000..eebe5fad0 --- /dev/null +++ b/testsuite/bzltestsuite/java/jflex/testing/annotations/BUILD @@ -0,0 +1,6 @@ +package(default_visibility = ["//testsuite:__subpackages__"]) + +java_library( + name = "annotations", + srcs = glob(["*.java"]), +) diff --git a/testsuite/bzltestsuite/java/jflex/testing/annotations/TestSpec.java b/testsuite/bzltestsuite/java/jflex/testing/annotations/TestSpec.java new file mode 100644 index 000000000..748cda924 --- /dev/null +++ b/testsuite/bzltestsuite/java/jflex/testing/annotations/TestSpec.java @@ -0,0 +1,11 @@ +package jflex.testing.annotations; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.RUNTIME) +public @interface TestSpec { + + /** Lex specification. */ + String lex(); +} diff --git a/testsuite/bzltestsuite/java/jflex/testing/javac/BUILD b/testsuite/bzltestsuite/java/jflex/testing/javac/BUILD new file mode 100644 index 000000000..978e7a4ad --- /dev/null +++ b/testsuite/bzltestsuite/java/jflex/testing/javac/BUILD @@ -0,0 +1,9 @@ +package(default_visibility = ["//testsuite:__subpackages__"]) + +java_library( + name = "javac", + srcs = glob(["*.java"]), + deps = [ + "//third_party/com/google/guava", + ], +) diff --git a/testsuite/bzltestsuite/java/jflex/testing/javac/CompilerException.java b/testsuite/bzltestsuite/java/jflex/testing/javac/CompilerException.java new file mode 100644 index 000000000..2c721db8a --- /dev/null +++ b/testsuite/bzltestsuite/java/jflex/testing/javac/CompilerException.java @@ -0,0 +1,55 @@ +package jflex.testing.javac; + +import com.google.common.base.Joiner; +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; +import java.util.List; +import java.util.Locale; +import java.util.stream.Collectors; +import javax.tools.Diagnostic; +import javax.tools.JavaFileObject; + +/** + * Exception while using the Java compiler. + * + * @author Régis Décamps + */ +public class CompilerException extends Exception { + + private final List> diagnostics; + + public CompilerException(List> diagnostics) { + super("javac exception: " + diagnosticCode(diagnostics)); + this.diagnostics = diagnostics; + } + + public CompilerException(Throwable e) { + super("javac exception: " + e.getMessage(), e); + this.diagnostics = ImmutableList.of(); + } + + public CompilerException(String message) { + super("javac exception: " + message); + this.diagnostics = ImmutableList.of(); + } + + @Override + public String toString() { + List diagnosticMessages = + diagnostics + .stream() + .map( + d -> + String.format( + "javac error: %s, line %d in file %s", + d.getMessage(Locale.ENGLISH), d.getLineNumber(), d.getSource().getName())) + .collect(Collectors.toList()); + return super.toString() + "\n" + Joiner.on('\n').join(diagnosticMessages); + } + + private static String diagnosticCode(List> diagnostics) { + Preconditions.checkArgument(!diagnostics.isEmpty(), "Empty diagnostic"); + Diagnostic firstDiagnostic = diagnostics.get(0); + return firstDiagnostic.getCode(); + } +} diff --git a/testsuite/bzltestsuite/java/jflex/testing/javac/JavacUtil.java b/testsuite/bzltestsuite/java/jflex/testing/javac/JavacUtil.java new file mode 100644 index 000000000..9b696f4b8 --- /dev/null +++ b/testsuite/bzltestsuite/java/jflex/testing/javac/JavacUtil.java @@ -0,0 +1,63 @@ +package jflex.testing.javac; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; +import javax.tools.DiagnosticCollector; +import javax.tools.JavaCompiler; +import javax.tools.JavaFileObject; +import javax.tools.StandardJavaFileManager; +import javax.tools.ToolProvider; + +/** + * Helper class to invoke the {@link JavaCompiler}. + * + * @author Régis Décamps + */ +public final class JavacUtil { + + /** Compiles the given java source files. */ + public static void compile(Iterable files) throws CompilerException { + JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); + DiagnosticCollector diagnostics = new DiagnosticCollector<>(); + StandardJavaFileManager fileManager = + compiler.getStandardFileManager(diagnostics, null, StandardCharsets.UTF_8); + + Iterable compilationUnit = + fileManager.getJavaFileObjectsFromFiles(files); + JavaCompiler.CompilationTask task = + compiler.getTask(null, fileManager, diagnostics, null, null, compilationUnit); + Boolean success = task.call(); + try { + if (!diagnostics.getDiagnostics().isEmpty()) { + throw new CompilerException(diagnostics.getDiagnostics()); + } + if (!Objects.equals(success, Boolean.TRUE)) { + // Something went wrong: the default DiagnosticListener should add a diagnostic entry. + // Anyway, let's fail to be sure. + throw new CompilerException("unknown error"); + } + } finally { + try { + fileManager.close(); + } catch (IOException ignore) { + } + } + } + + /** + * Compiles the given java source files. + * + * @see #compile(Iterable) + */ + public static void compile(List javaSourceFileNames) throws CompilerException { + List javaSourceFiles = + javaSourceFileNames.stream().map(File::new).collect(Collectors.toList()); + compile(javaSourceFiles); + } + + private JavacUtil() {} +} diff --git a/testsuite/javatests/README.md b/testsuite/javatests/README.md new file mode 100644 index 000000000..07e7fc129 --- /dev/null +++ b/testsuite/javatests/README.md @@ -0,0 +1,3 @@ +# Regression tests for JFlex [WIP] + +Migration of the testcases to Bazel bzltestsuite. diff --git a/testsuite/javatests/jflex/testcase/apipirivate/ApiPrivateTest.java b/testsuite/javatests/jflex/testcase/apipirivate/ApiPrivateTest.java new file mode 100644 index 000000000..69655eb8f --- /dev/null +++ b/testsuite/javatests/jflex/testcase/apipirivate/ApiPrivateTest.java @@ -0,0 +1,31 @@ +package jflex.testcase.apipirivate; + +import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.fail; + +import com.google.common.collect.ImmutableList; +import jflex.testing.JFlexTestRunner; +import jflex.testing.annotations.TestSpec; +import jflex.testing.javac.CompilerException; +import jflex.testing.javac.JavacUtil; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** test feature request #513500 [Generate cleaner interfaces], %apiprivate option */ +@RunWith(JFlexTestRunner.class) +@TestSpec(lex = "testsuite/javatests/jflex/testcase/apipirivate/private.flex") +public class ApiPrivateTest { + + @Test + public void compile() throws CompilerException { + try { + JavacUtil.compile( + ImmutableList.of( + "testsuite/javatests/jflex/testcase/apipirivate/Private.java", + "testsuite/javatests/jflex/testcase/apipirivate/AttemptPrivateAccess.java")); + fail("Class `Private` should have private access"); + } catch (CompilerException e) { + assertThat(e).hasMessageThat().contains("compiler.err.report.access"); + } + } +} diff --git a/testsuite/javatests/jflex/testcase/apipirivate/AttemptPrivateAccess.java b/testsuite/javatests/jflex/testcase/apipirivate/AttemptPrivateAccess.java new file mode 100755 index 000000000..3c5ea01bb --- /dev/null +++ b/testsuite/javatests/jflex/testcase/apipirivate/AttemptPrivateAccess.java @@ -0,0 +1,13 @@ +package jflex.testcase.apipirivate; + +import java.io.InputStreamReader; + +public final class AttemptPrivateAccess { + + public static void main(String argv[]) { + Private s = new Private(new InputStreamReader(System.in)); + s.yylex(); + } + + private AttemptPrivateAccess() {} +} diff --git a/testsuite/javatests/jflex/testcase/apipirivate/BUILD b/testsuite/javatests/jflex/testcase/apipirivate/BUILD new file mode 100644 index 000000000..d83593e8c --- /dev/null +++ b/testsuite/javatests/jflex/testcase/apipirivate/BUILD @@ -0,0 +1,15 @@ +java_test( + name = "ApiPrivateTest", + srcs = ["ApiPrivateTest.java"], + data = [ + "AttemptPrivateAccess.java", + "private.flex", + ], + deps = [ + "//testsuite/bzltestsuite", + "//testsuite/bzltestsuite/java/jflex/testing/annotations", + "//testsuite/bzltestsuite/java/jflex/testing/javac", + "//third_party/com/google/guava", + "//third_party/com/google/truth", + ], +) diff --git a/testsuite/javatests/jflex/testcase/apipirivate/private.flex b/testsuite/javatests/jflex/testcase/apipirivate/private.flex new file mode 100755 index 000000000..236118803 --- /dev/null +++ b/testsuite/javatests/jflex/testcase/apipirivate/private.flex @@ -0,0 +1,13 @@ +package jflex.testcase.apipirivate; + +%% + +%public +%class Private + +%apiprivate +%int + +%% + +[^] { /* no action */ } diff --git a/testsuite/jflex-testsuite-maven-plugin/pom.xml b/testsuite/jflex-testsuite-maven-plugin/pom.xml index 554c63e00..a2ee8237a 100644 --- a/testsuite/jflex-testsuite-maven-plugin/pom.xml +++ b/testsuite/jflex-testsuite-maven-plugin/pom.xml @@ -64,37 +64,27 @@ - windows_profile + + jdk_hack - - Windows - + (,1.8] - - ${java.home}/../lib/tools.jar - - - - unix_profile - - - Unix - - - - ${java.home}/../lib/tools.jar - - - - osx_profile - - - mac - - - - ${java.home}/../lib/tools.jar - + + + sun.jdk + tools + 1.6.0 + system + ${java.home}/../lib/tools.jar + + @@ -115,19 +105,6 @@ jflex ${project.version} - - - - - - - - sun.jdk - tools - 1.5.0 - system - ${toolsjar} - org.apache.maven.plugin-tools maven-plugin-annotations diff --git a/testsuite/jflex-testsuite-maven-plugin/src/main/java/jflextest/CustomClassLoader.java b/testsuite/jflex-testsuite-maven-plugin/src/main/java/jflextest/CustomClassLoader.java index 61c848cb7..1cfffc93a 100755 --- a/testsuite/jflex-testsuite-maven-plugin/src/main/java/jflextest/CustomClassLoader.java +++ b/testsuite/jflex-testsuite-maven-plugin/src/main/java/jflextest/CustomClassLoader.java @@ -2,10 +2,11 @@ import java.io.ByteArrayOutputStream; import java.io.File; -import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; import java.util.StringTokenizer; @@ -36,9 +37,9 @@ public CustomClassLoader(File... files) { } /** - * Break up a class path string into its items and store in pathItems. + * Break up a class path string into its items and store in {@code pathItems}. * - *

Uses system property path.separator as delimiter. + *

Uses system property {@code path.separator} as delimiter. */ private void scanPath(String classPath) { if (classPath == null) return; @@ -134,8 +135,7 @@ boolean isJar(String pathEntry) { private byte[] loadFileData(String path, String fileName) { File file = new File(path, fileName); if (file.canRead()) { - try { - FileInputStream stream = new FileInputStream(file); + try (InputStream stream = Files.newInputStream(Paths.get(file.toString()))) { ByteArrayOutputStream out = new ByteArrayOutputStream(1000); byte[] b = new byte[1000]; int n; @@ -144,7 +144,7 @@ private byte[] loadFileData(String path, String fileName) { out.close(); return out.toByteArray(); } catch (IOException e) { - // ignore, maybe another path item succeds + // ignore, maybe another path item succeeds } } return null; @@ -166,7 +166,7 @@ private InputStream getFileEntry(String name, String path) { File file = new File(path, name); if (file.canRead()) { try { - return new FileInputStream(file); + return Files.newInputStream(Paths.get(file.toString())); } catch (IOException e) { return null; } diff --git a/testsuite/jflex-testsuite-maven-plugin/src/main/java/jflextest/DiffStream.java b/testsuite/jflex-testsuite-maven-plugin/src/main/java/jflextest/DiffStream.java index 065c05026..0f7432d04 100755 --- a/testsuite/jflex-testsuite-maven-plugin/src/main/java/jflextest/DiffStream.java +++ b/testsuite/jflex-testsuite-maven-plugin/src/main/java/jflextest/DiffStream.java @@ -6,6 +6,7 @@ import java.io.StringReader; import java.util.ArrayList; import java.util.List; +import java.util.Objects; class DiffStream { @@ -22,7 +23,7 @@ public DiffStream() {} */ private boolean match(String s1, String s2) { if (s1 == null) return s2 == null; - return s1.replace('\\', '/').equals(s2.replace('\\', '/')); + return Objects.equals(s1.replace('\\', '/'), s2.replace('\\', '/')); } public String diff(Reader input, Reader expected) { @@ -32,7 +33,7 @@ public String diff(Reader input, Reader expected) { /** * Compares to streams line by line. * - * @return null if they are the same, a String error message otherwise + * @return {@code null} if they are the same, a String error message otherwise * @param diffLines a List of Integer. The line numbers where differences are expected (and to be * ignored) * @param input the input stream diff --git a/testsuite/jflex-testsuite-maven-plugin/src/main/java/jflextest/Exec.java b/testsuite/jflex-testsuite-maven-plugin/src/main/java/jflextest/Exec.java index 238fac695..7429c4db5 100755 --- a/testsuite/jflex-testsuite-maven-plugin/src/main/java/jflextest/Exec.java +++ b/testsuite/jflex-testsuite-maven-plugin/src/main/java/jflextest/Exec.java @@ -1,5 +1,6 @@ package jflextest; +import com.google.common.base.Joiner; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileNotFoundException; @@ -44,21 +45,29 @@ private static String[] toArray(List a, List b) { } /** - * Call javac on toCompile in input dir. If toCompile is null, all *.java files below dir will be - * compiled. + * Call javac on javaSourceFiles in input dir. If javaSourceFiles is {@code null}, all {@code + * *.java} files in the directory will be compiled. + * + * @param javaSourceFiles A list of files to compile, or {@code null} + * @param dir Source directory. */ public static TestResult execJavac( - String toCompile, File dir, String additionalJars, String encoding) { + List javaSourceFiles, File dir, String additionalJars, String encoding) + throws FileNotFoundException { + // javac fails if an input file doesn't exist + checkFilesExist(javaSourceFiles, dir); Project p = new Project(); - Javac javac = new Javac(); Path path = new Path(p, dir.toString()); + Javac javac = new Javac(); javac.setProject(p); javac.setSrcdir(path); javac.setDestdir(dir); javac.setTarget(JAVA_VERSION); javac.setSource(JAVA_VERSION); javac.setSourcepath(new Path(p, "")); // Only compile explicitly specified source files - javac.setIncludes(toCompile); + if (javaSourceFiles != null) { + javac.setIncludes(Joiner.on(' ').join(javaSourceFiles)); + } javac.setEncoding(encoding); Path classPath = javac.createClasspath(); // Locate the jflex jar in the user's Maven local repository @@ -72,8 +81,7 @@ public static TestResult execJavac( javac.execute(); return new TestResult(out.toString(), true); } catch (BuildException e) { - return new TestResult( - "Compilation with classpath " + classPath + NL + e + NL + out.toString(), false); + return new TestResult(e + NL + out.toString() + NL + "classpath: " + classPath, false); } finally { System.setErr(outSafe); } @@ -170,4 +178,20 @@ public static TestResult execClass( return new TestResult(out.toString(outputFileEncoding), success); } + + /** + * Checks that all files exist in the given directory. + * + * @throws FileNotFoundException when a file doesn't exist. + */ + private static void checkFilesExist(List files, File dir) throws FileNotFoundException { + if (files != null) { + for (String src : files) { + File f = new File(dir, src); + if (!f.isFile()) { + throw new FileNotFoundException(f.getPath()); + } + } + } + } } diff --git a/testsuite/jflex-testsuite-maven-plugin/src/main/java/jflextest/JFlexTestsuiteMojo.java b/testsuite/jflex-testsuite-maven-plugin/src/main/java/jflextest/JFlexTestsuiteMojo.java index e260ca32e..94926ad54 100755 --- a/testsuite/jflex-testsuite-maven-plugin/src/main/java/jflextest/JFlexTestsuiteMojo.java +++ b/testsuite/jflex-testsuite-maven-plugin/src/main/java/jflextest/JFlexTestsuiteMojo.java @@ -32,8 +32,7 @@ public class JFlexTestsuiteMojo extends AbstractMojo { /** Path of the JFlex uber jar under test. */ @Parameter( - defaultValue = "${project.parent.basedir}/jflex/target/jflex-full-${project.version}.jar" - ) + defaultValue = "${project.parent.basedir}/jflex/target/jflex-full-${project.version}.jar") private String jflexUberJarFilename; /** */ @@ -69,7 +68,7 @@ public void execute() throws MojoExecutionException, MojoFailureException { success = Tester.runTests(files, jflexUberJar); } catch (Exception e) { - throw new MojoExecutionException("Failed to execute test suite", e); + throw new MojoExecutionException("Failed to execute test suite: " + e.getMessage(), e); } if (!success) { throw new MojoFailureException("Test(s) failed."); diff --git a/testsuite/jflex-testsuite-maven-plugin/src/main/java/jflextest/TestCase.java b/testsuite/jflex-testsuite-maven-plugin/src/main/java/jflextest/TestCase.java index 4c1561580..bff991aff 100755 --- a/testsuite/jflex-testsuite-maven-plugin/src/main/java/jflextest/TestCase.java +++ b/testsuite/jflex-testsuite-maven-plugin/src/main/java/jflextest/TestCase.java @@ -2,16 +2,20 @@ import com.google.common.collect.ImmutableList; import java.io.File; -import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileReader; +import java.io.IOException; import java.io.InputStreamReader; import java.io.StringReader; import java.io.UnsupportedEncodingException; +import java.nio.file.Files; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Locale; +import java.util.Objects; +import org.apache.maven.plugin.MojoFailureException; public class TestCase { @@ -21,8 +25,8 @@ public class TestCase { /** files on which to invoke jflex */ private List jflexFiles; - /** command line switches for javac invocation */ - private List javacExtraFiles; + /** command line switches for javac invocation, instead of the default {@code .java}. */ + private List javacFiles; /** lines with expected differences in jflex output */ private List jflexDiff; @@ -59,6 +63,7 @@ public class TestCase { /** get- set- methods */ void setTestName(String s) { testName = s; + // TODO(regisd): The class name should depend on the flex `%class`, not on the test name. className = testName.substring(0, 1).toUpperCase(Locale.ENGLISH) + testName.substring(1); } @@ -82,8 +87,8 @@ void setJflexCmdln(List v) { jflexCmdln = v; } - void setJavacExtraFiles(List v) { - javacExtraFiles = v; + void setJavacFiles(List v) { + javacFiles = v; } private void setInputOutput(List v) { @@ -125,7 +130,7 @@ void init(File testDir) { String name; for (String file : testDir.list()) { if (null != commonInputFile) { - if (file.equals(testName + ".output")) { + if (Objects.equals(file, testName + ".output")) { temp.add(new InputOutput((new File(testDir, testName)).toString(), true)); commonInputFile = (new File(testDir, commonInputFile)).toString(); } @@ -148,8 +153,17 @@ void init(File testDir) { testPath = testDir; } - void createScanner(File jflexUberJar) throws TestFailException { - jflexFiles.add((new File(testPath, testName + ".flex")).getPath()); + void createScanner(File jflexUberJar, boolean verbose) + throws TestFailException, MojoFailureException { + File lexFile = new File(testPath, testName + ".flex"); + if (verbose) { + System.out.println(String.format("Open lex specification [%s]", lexFile)); + } + if (!lexFile.exists()) { + throw new MojoFailureException( + "Cannot open lex definition", new FileNotFoundException(lexFile.getPath())); + } + jflexFiles.add(lexFile.getPath()); // invoke JFlex TestResult jflexResult = Exec.execJFlex(jflexCmdln, jflexFiles); // System.out.println(jflexResult); @@ -189,33 +203,30 @@ void createScanner(File jflexUberJar) throws TestFailException { } // Compile Scanner - StringBuilder builder = new StringBuilder(); - builder.append(new File(testPath, className + ".java").getName()); - if (null != javacExtraFiles) { - for (String extraFile : javacExtraFiles) { - builder.append(',').append(extraFile); - } - } - String toCompile = builder.toString(); - if (Tester.verbose) { - System.out.println("File(s) to Compile: " + toCompile); - } - TestResult javacResult = - Exec.execJavac(toCompile, testPath, jflexUberJar.getAbsolutePath(), javacEncoding); - - // System.out.println(javacResult); + final List toCompile = getFilesToCompile(); if (Tester.verbose) { - System.out.println( - "Compilation successful: " - + javacResult.getSuccess() - + " [expected: " - + !expectJavacFail - + "]"); + System.out.println("File(s) to compile: " + toCompile); } - if (javacResult.getSuccess() == expectJavacFail) { - System.out.println("Compilation failed in " + testPath + " for " + toCompile); - System.out.println(javacResult.getOutput()); - throw new TestFailException(); + try { + TestResult javacResult = + Exec.execJavac(toCompile, testPath, jflexUberJar.getAbsolutePath(), javacEncoding); + + // System.out.println(javacResult); + if (Tester.verbose) { + System.out.println( + "Compilation successful: " + + javacResult.getSuccess() + + " [expected: " + + !expectJavacFail + + "]"); + } + if (javacResult.getSuccess() == expectJavacFail) { + throw new TestFailException( + "Compilation failed in " + testPath + " for " + toCompile, + new Exception(javacResult.getOutput())); + } + } catch (FileNotFoundException e) { + throw new TestFailException("javac: file not found: ", e); } } else { if (!expectJFlexFail) { @@ -247,6 +258,20 @@ void createScanner(File jflexUberJar) throws TestFailException { } } + /** Returns the list of java files to compile. */ + private List getFilesToCompile() { + if (javacFiles == null) { + return ImmutableList.of(new File(testPath, className + ".java").getName()); + } else { + ImmutableList.Builder builder = ImmutableList.builder(); + for (String explicitJavaSrc : javacFiles) { + File f = new File(testPath, explicitJavaSrc); + builder.add(f.getName()); + } + return builder.build(); + } + } + boolean hasMoreToDo() { // check if there's more files to run scanner main on return !(inputOutput.isEmpty()); @@ -281,18 +306,14 @@ void runNext(File jflexUberJar) throws TestFailException, UnsupportedEncodingExc if (expected.exists()) { DiffStream check = new DiffStream(); String diff; - try { + try (InputStreamReader expectedContent = + new InputStreamReader( + Files.newInputStream(Paths.get(expected.toString())), outputFileEncoding)) { diff = - check.diff( - jflexDiff, - new StringReader(classExecResult.getOutput()), - new InputStreamReader(new FileInputStream(expected), outputFileEncoding)); - } catch (FileNotFoundException e) { + check.diff(jflexDiff, new StringReader(classExecResult.getOutput()), expectedContent); + } catch (IOException e) { System.out.println("Error opening file " + expected); throw new TestFailException(); - } catch (UnsupportedEncodingException e) { - System.out.println("Unsupported encoding '" + outputFileEncoding + "'"); - throw new TestFailException(); } if (diff != null) { System.out.println("Test failed, unexpected output: " + diff); @@ -307,7 +328,7 @@ void runNext(File jflexUberJar) throws TestFailException, UnsupportedEncodingExc } public String toString() { - return "Testname: " + return "Test name: " + testName + "\nDescription: " + description @@ -318,9 +339,7 @@ public String toString() { + "\n" + "JFlex Command line: " + jflexCmdln - + (null != javacExtraFiles - ? " Javac Extra Files: " + Arrays.toString(javacExtraFiles.toArray()) - : "") + + (null != javacFiles ? " Javac Files: " + Arrays.toString(javacFiles.toArray()) : "") + "\n" + "Files to run Tester on " + inputOutput diff --git a/testsuite/jflex-testsuite-maven-plugin/src/main/java/jflextest/TestFailException.java b/testsuite/jflex-testsuite-maven-plugin/src/main/java/jflextest/TestFailException.java index 9d539bc2a..799e23eff 100755 --- a/testsuite/jflex-testsuite-maven-plugin/src/main/java/jflextest/TestFailException.java +++ b/testsuite/jflex-testsuite-maven-plugin/src/main/java/jflextest/TestFailException.java @@ -9,4 +9,8 @@ public TestFailException() { public TestFailException(String message) { super(message); } + + public TestFailException(String message, Throwable cause) { + super(message, cause); + } } diff --git a/testsuite/jflex-testsuite-maven-plugin/src/main/java/jflextest/Tester.java b/testsuite/jflex-testsuite-maven-plugin/src/main/java/jflextest/Tester.java index d1391b0de..d3d2cf38b 100755 --- a/testsuite/jflex-testsuite-maven-plugin/src/main/java/jflextest/Tester.java +++ b/testsuite/jflex-testsuite-maven-plugin/src/main/java/jflextest/Tester.java @@ -3,9 +3,10 @@ import java.io.File; import java.io.FileReader; import java.io.FilenameFilter; -import java.io.IOException; import java.util.ArrayList; import java.util.List; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; public class Tester { @@ -65,7 +66,8 @@ public boolean accept(File f, String name) { * @param jflexUberJar The JFlex shaded jar * @return true if all tests succeeded, false otherwise */ - public static boolean runTests(List tests, File jflexUberJar) { + public static boolean runTests(List tests, File jflexUberJar) + throws TestFailException, MojoExecutionException, MojoFailureException { int successCount = 0; int totalCount = 0; @@ -88,7 +90,7 @@ public static boolean runTests(List tests, File jflexUberJar) { if (verbose) System.out.println("Loaded successfully"); // - Details:\n"+currentTest); if (currentTest.checkJavaVersion()) { - currentTest.createScanner(jflexUberJar); + currentTest.createScanner(jflexUberJar, verbose); while (currentTest.hasMoreToDo()) currentTest.runNext(jflexUberJar); successCount++; @@ -98,13 +100,9 @@ public static boolean runTests(List tests, File jflexUberJar) { System.out.println("Test [" + test + "] skipped (JDK version mismatch)."); } } catch (TestFailException e) { - System.out.println("Test [" + test + "] failed!"); - } catch (LoadException e) { - System.out.println("Load Error:" + e.getMessage()); - } catch (IOException e) { - System.out.println("IO Error:" + e.getMessage()); + throw new MojoFailureException("Test [" + test + "] failed", e); } catch (Exception e) { - e.printStackTrace(); + throw new MojoExecutionException("Test [" + test.getName() + "] failed to execute", e); } } diff --git a/testsuite/jflex-testsuite-maven-plugin/src/main/jflex/TestLoader.flex b/testsuite/jflex-testsuite-maven-plugin/src/main/jflex/TestLoader.flex index 5ab21bfb1..0d89dfdae 100644 --- a/testsuite/jflex-testsuite-maven-plugin/src/main/jflex/TestLoader.flex +++ b/testsuite/jflex-testsuite-maven-plugin/src/main/jflex/TestLoader.flex @@ -14,7 +14,7 @@ import jflex.sym; // %debug -%state DESCR JFLEXCMD JAVAC_EXTRA_FILES LINELIST VERSION +%state DESCR JFLEXCMD JAVAC_FILES LINELIST VERSION %{ private StringBuilder buffer = new StringBuilder(); @@ -34,7 +34,7 @@ DIGIT = [0-9] "description:" { yybegin(DESCR); } "jflex: " { cmdLine = new ArrayList(); yybegin(JFLEXCMD); } - "javac-extra-files: " { cmdLine = new ArrayList(); yybegin(JAVAC_EXTRA_FILES); } + "javac-files: " { cmdLine = new ArrayList(); yybegin(JAVAC_FILES); } "jflex-fail:" " "+ "true" { test.setExpectJFlexFail(true); } "jflex-fail:" " "+ "false" { test.setExpectJFlexFail(false); } @@ -72,7 +72,7 @@ DIGIT = [0-9] } - { + { [^ \t\r\n]+ { cmdLine.add(yytext()); } \" ~\" { cmdLine.add(yytext().substring(1,yylength()-1)); /* quoted cmdline options */ } @@ -83,8 +83,8 @@ DIGIT = [0-9] {NL} { test.setJflexCmdln(cmdLine); yybegin(YYINITIAL); } - - {NL} { test.setJavacExtraFiles(cmdLine); yybegin(YYINITIAL); } + + {NL} { test.setJavacFiles(cmdLine); yybegin(YYINITIAL); } { [0-9]+ { lineList.add(new Integer(yytext())); } diff --git a/testsuite/testcases/.gitignore b/testsuite/testcases/.gitignore deleted file mode 100644 index 337e58f45..000000000 --- a/testsuite/testcases/.gitignore +++ /dev/null @@ -1,64 +0,0 @@ -src/test/cases/apiprivate/Private.java -src/test/cases/arr-return/Arr.java -src/test/cases/bol/Bol.java -src/test/cases/buffer/Buffer.java -src/test/cases/caseless-jflex/Caseless.java -src/test/cases/caseless-jlex/Caseless.java -src/test/cases/ccl-16/Ccl.java -src/test/cases/ccl-bug/Yylex.java -src/test/cases/ccl-esc/Yylex.java -src/test/cases/ccl-pre/Ccl2.java -src/test/cases/ccl-pre/Ccl3.java -src/test/cases/charclass/Charclass.java -src/test/cases/count/Count.java -src/test/cases/ctorarg/Ctorarg.java -src/test/cases/cupsym/Cupsym.java -src/test/cases/dot-newline/Dotnewline.java -src/test/cases/empty-match/*.java -src/test/cases/eof-min/Eofmin.java -src/test/cases/eofclose/Eofclose.java -src/test/cases/eofclose2/Eofclose2.java -src/test/cases/eol-comment/Eolcomment.java -src/test/cases/eol-look/Eol.java -src/test/cases/fixed-look/Fixedlook.java -src/test/cases/generics/Generics*.java -src/test/cases/genlook/Genlook.java -src/test/cases/genlook2/Genlook2.java -src/test/cases/include/Include.java -src/test/cases/include-in-rules/IncludeInRules.java -src/test/cases/include2/Include2.java -src/test/cases/initthrow-eol/Initthrow.java -src/test/cases/java/Java.java -src/test/cases/line-cont/Yylex.java -src/test/cases/look/Look.java -src/test/cases/look-macro/Lookmacro.java -src/test/cases/macro-exp/Macro.java -src/test/cases/manual-ex/Lexer.java -src/test/cases/nevermatch/Never.java -src/test/cases/simple/Simple.java -src/test/cases/six-digit-unicode-escape/SixDigitUnicodeEscape*.java -src/test/cases/testloader/Testloader.java -src/test/cases/unicode-age/UnicodeAge_*.java -src/test/cases/unicode-blocks/Unicode*.java -src/test/cases/unicode-caseless/UnicodeCaseless*.java -src/test/cases/unicode-codepoint-escapes/UnicodeCodePointEscapes*.java -src/test/cases/unicode-compatibility-properties/UnicodeCompatibilityProperties*.java -src/test/cases/unicode-derived-core-properties/UnicodeDerivedCoreProperties*.java -src/test/cases/unicode-digit/UnicodeDecimalDigit*.java -src/test/cases/unicode-general-category/UnicodeGeneralCategory*.java -src/test/cases/unicode-general-category/UnicodeNotGeneralCategory*.java -src/test/cases/unicode-grapheme-break/UnicodeGrapheme*Break*.java -src/test/cases/unicode-letter/UnicodeLetter*.java -src/test/cases/unicode-line-break/UnicodeLineBreak*.java -src/test/cases/unicode-lowercase/UnicodeLowercase*.java -src/test/cases/unicode-misc-properties/UnicodeMisc*.java -src/test/cases/unicode-proplist/UnicodePropList*.java -src/test/cases/unicode-scripts/Unicode*.java -src/test/cases/unicode-sentence-break/UnicodeSentenceBreak*.java -src/test/cases/unicode-space/UnicodeWhiteSpace*.java -src/test/cases/unicode-uppercase/UnicodeUppercase*.java -src/test/cases/unicode-word/UnicodeWord*.java -src/test/cases/unicode-word-break/UnicodeWordBreak*.java -src/test/cases/cup2private/Cup2private.java -src/test/cases/no-unused/NoUnused.java -src/test/cases/unused/Unused.java diff --git a/testsuite/testcases/README.md b/testsuite/testcases/README.md new file mode 100644 index 000000000..3b59bff6a --- /dev/null +++ b/testsuite/testcases/README.md @@ -0,0 +1,8 @@ +# JFlex regression tests + +Run one particular test case with the `testcases` variable. + +Example: + + ../../mvnw test -Dtestcases=apiprivate + diff --git a/testsuite/testcases/src/test/cases/action-pipe/.gitignore b/testsuite/testcases/src/test/cases/action-pipe/.gitignore new file mode 100644 index 000000000..74e6d5f15 --- /dev/null +++ b/testsuite/testcases/src/test/cases/action-pipe/.gitignore @@ -0,0 +1 @@ +ActionPipe.java diff --git a/testsuite/testcases/src/test/cases/action-pipe/action-pipe.flex b/testsuite/testcases/src/test/cases/action-pipe/action-pipe.flex new file mode 100644 index 000000000..7aa6fa2a1 --- /dev/null +++ b/testsuite/testcases/src/test/cases/action-pipe/action-pipe.flex @@ -0,0 +1,25 @@ + +%% + +%public +%class ActionPipe + +%int + +LineTerminator = \r|\n|\r\n + +Identifier = [:jletter:][:jletterdigit:]* + + +%% + + +Identifier { System.out.println(yytext()); } + +<> | +LineTerminator { System.out.println("⏎\n"); } + + +[^] { /* no action */ } + + diff --git a/testsuite/testcases/src/test/cases/action-pipe/action-pipe.output b/testsuite/testcases/src/test/cases/action-pipe/action-pipe.output new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/testsuite/testcases/src/test/cases/action-pipe/action-pipe.output @@ -0,0 +1 @@ + diff --git a/testsuite/testcases/src/test/cases/action-pipe/action-pipe.test b/testsuite/testcases/src/test/cases/action-pipe/action-pipe.test new file mode 100644 index 000000000..735e7fab0 --- /dev/null +++ b/testsuite/testcases/src/test/cases/action-pipe/action-pipe.test @@ -0,0 +1,7 @@ +name: action-pipe + +description: +Issue #201. Pipe action doesn't compile when first. + +# TODO(#201) Fix the pipe action on EOF +jflex-fail: true diff --git a/testsuite/testcases/src/test/cases/apiprivate/.gitignore b/testsuite/testcases/src/test/cases/apiprivate/.gitignore new file mode 100644 index 000000000..23e89acf3 --- /dev/null +++ b/testsuite/testcases/src/test/cases/apiprivate/.gitignore @@ -0,0 +1 @@ +Private.java diff --git a/testsuite/testcases/src/test/cases/apiprivate/private.test b/testsuite/testcases/src/test/cases/apiprivate/private.test index a49a3d758..cf1d8be88 100755 --- a/testsuite/testcases/src/test/cases/apiprivate/private.test +++ b/testsuite/testcases/src/test/cases/apiprivate/private.test @@ -6,6 +6,7 @@ test feature request #513500 [Generate cleaner interfaces], %apiprivate option jflex: -q -javac-extra-files: Test.java +# Verify that the Test class doesn't have access to the Private class, +javac-files: Private.java Test.java javac-fail: true diff --git a/testsuite/testcases/src/test/cases/arr-return/.gitignore b/testsuite/testcases/src/test/cases/arr-return/.gitignore new file mode 100644 index 000000000..364cc5a50 --- /dev/null +++ b/testsuite/testcases/src/test/cases/arr-return/.gitignore @@ -0,0 +1 @@ +Arr.java diff --git a/testsuite/testcases/src/test/cases/bol/.gitignore b/testsuite/testcases/src/test/cases/bol/.gitignore new file mode 100644 index 000000000..0d7dc2c74 --- /dev/null +++ b/testsuite/testcases/src/test/cases/bol/.gitignore @@ -0,0 +1 @@ +Bol.java diff --git a/testsuite/testcases/src/test/cases/buffer/.gitignore b/testsuite/testcases/src/test/cases/buffer/.gitignore new file mode 100644 index 000000000..16ad15c13 --- /dev/null +++ b/testsuite/testcases/src/test/cases/buffer/.gitignore @@ -0,0 +1 @@ +Buffer.java diff --git a/testsuite/testcases/src/test/cases/caseless-jflex/.gitignore b/testsuite/testcases/src/test/cases/caseless-jflex/.gitignore new file mode 100644 index 000000000..641dfd2ff --- /dev/null +++ b/testsuite/testcases/src/test/cases/caseless-jflex/.gitignore @@ -0,0 +1 @@ +Caseless.java diff --git a/testsuite/testcases/src/test/cases/caseless-jlex/.gitignore b/testsuite/testcases/src/test/cases/caseless-jlex/.gitignore new file mode 100644 index 000000000..641dfd2ff --- /dev/null +++ b/testsuite/testcases/src/test/cases/caseless-jlex/.gitignore @@ -0,0 +1 @@ +Caseless.java diff --git a/testsuite/testcases/src/test/cases/ccl-16/.gitignore b/testsuite/testcases/src/test/cases/ccl-16/.gitignore new file mode 100644 index 000000000..9c8a22fb8 --- /dev/null +++ b/testsuite/testcases/src/test/cases/ccl-16/.gitignore @@ -0,0 +1 @@ +Ccl.java diff --git a/testsuite/testcases/src/test/cases/ccl-bug/.gitignore b/testsuite/testcases/src/test/cases/ccl-bug/.gitignore new file mode 100644 index 000000000..bae521e23 --- /dev/null +++ b/testsuite/testcases/src/test/cases/ccl-bug/.gitignore @@ -0,0 +1 @@ +Yylex.java diff --git a/testsuite/testcases/src/test/cases/ccl-bug/ccl-bug.test b/testsuite/testcases/src/test/cases/ccl-bug/ccl-bug.test index 03d8c6bf9..207e84aa2 100644 --- a/testsuite/testcases/src/test/cases/ccl-bug/ccl-bug.test +++ b/testsuite/testcases/src/test/cases/ccl-bug/ccl-bug.test @@ -5,3 +5,5 @@ description: Generated Yylex.java does not compile, because of missing "," jflex: --nobak + +javac-files: Yylex.java diff --git a/testsuite/testcases/src/test/cases/ccl-esc/.gitignore b/testsuite/testcases/src/test/cases/ccl-esc/.gitignore new file mode 100644 index 000000000..bae521e23 --- /dev/null +++ b/testsuite/testcases/src/test/cases/ccl-esc/.gitignore @@ -0,0 +1 @@ +Yylex.java diff --git a/testsuite/testcases/src/test/cases/ccl-esc/ccl.test b/testsuite/testcases/src/test/cases/ccl-esc/ccl.test index 793ce2c77..e31191c67 100644 --- a/testsuite/testcases/src/test/cases/ccl-esc/ccl.test +++ b/testsuite/testcases/src/test/cases/ccl-esc/ccl.test @@ -4,3 +4,5 @@ description: bug-test for [".."] style character escapes (#467469) jflex: --nobak --dump + +javac-files: Yylex.java diff --git a/testsuite/testcases/src/test/cases/ccl-pre/.gitignore b/testsuite/testcases/src/test/cases/ccl-pre/.gitignore new file mode 100644 index 000000000..b8deb723e --- /dev/null +++ b/testsuite/testcases/src/test/cases/ccl-pre/.gitignore @@ -0,0 +1,2 @@ +Ccl2.java +Ccl3.java diff --git a/testsuite/testcases/src/test/cases/charclass/.gitignore b/testsuite/testcases/src/test/cases/charclass/.gitignore new file mode 100644 index 000000000..1af69435a --- /dev/null +++ b/testsuite/testcases/src/test/cases/charclass/.gitignore @@ -0,0 +1 @@ +Charclass.java diff --git a/testsuite/testcases/src/test/cases/count/.gitignore b/testsuite/testcases/src/test/cases/count/.gitignore new file mode 100644 index 000000000..772276e5b --- /dev/null +++ b/testsuite/testcases/src/test/cases/count/.gitignore @@ -0,0 +1 @@ +Count.java diff --git a/testsuite/testcases/src/test/cases/ctorarg/.gitignore b/testsuite/testcases/src/test/cases/ctorarg/.gitignore new file mode 100644 index 000000000..c7aa701c1 --- /dev/null +++ b/testsuite/testcases/src/test/cases/ctorarg/.gitignore @@ -0,0 +1 @@ +Ctorarg.java diff --git a/testsuite/testcases/src/test/cases/cup2private/.gitignore b/testsuite/testcases/src/test/cases/cup2private/.gitignore new file mode 100644 index 000000000..ac8a23e60 --- /dev/null +++ b/testsuite/testcases/src/test/cases/cup2private/.gitignore @@ -0,0 +1 @@ +Cup2private.java diff --git a/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/ant/CUP2AntTask.java b/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/ant/CUP2AntTask.java index e1aef6b56..01ddd2116 100644 --- a/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/ant/CUP2AntTask.java +++ b/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/ant/CUP2AntTask.java @@ -1,12 +1,5 @@ package edu.tum.cup2.ant; -import java.io.File; -import java.util.LinkedList; - -import org.apache.tools.ant.*; -import org.apache.tools.ant.types.Path; -import org.apache.tools.ant.taskdefs.Javac; - import edu.tum.cup2.generator.*; import edu.tum.cup2.generator.exceptions.GeneratorException; import edu.tum.cup2.generator.exceptions.ReduceReduceConflict; @@ -21,418 +14,316 @@ import edu.tum.cup2.parser.tables.LRParsingTable; import edu.tum.cup2.spec.CUP2Specification; import edu.tum.cup2.spec.exceptions.IllegalSpecException; - +import java.io.File; +import java.util.LinkedList; +import java.util.Objects; +import org.apache.tools.ant.*; +import org.apache.tools.ant.taskdefs.Javac; +import org.apache.tools.ant.types.Path; /** - * Custom CUP2 compile task for Ant. - * The task can be used to create a parser using CUP2 with a {@link CUP2Specification}. - * Therefore it creates the class files for the parser with - * the help of the javac task and creates the .cup2 file (serialization of the parser) - * using the {@link LRParserSerializer} class. - * - * See CUP2AntTask.txt for more information. - * + * Custom CUP2 compile task for Ant. The task can be used to create a parser using CUP2 with a + * {@link CUP2Specification}. Therefore it creates the class files for the parser with the help of + * the javac task and creates the .cup2 file (serialization of the parser) using the {@link + * LRParserSerializer} class. + * + *

See CUP2AntTask.txt for more information. + * * @author Michael Hausmann * @author Andreas Wenger */ -public class CUP2AntTask - extends Task -{ - - //required attributes - private String spec = null; //specification name; e.g: edu.tum.cup2.test.SpecCalc4 - private String specdir = null; //root source directory for the specification - private String destdir = null; //destination directory for CUP2 and the compiled spec - private Path classpath = null; - - //optional attributes - private String cup2srcdir = null; //source path of cup2 - private String algorithm = null; //generator algorithm; e.g: lr1 - private boolean verbose = false; //print a lot of status messages? - private String parsetable = null; //where to store the parsing table? - - //other fields - private CUP2Specification cup2Specification = null; //instantiated specification - - - /** - * executing the cup2 task - */ - public void execute() - { - //print parameter settings - if (this.verbose) - this.displayValues(); - - //check if required attribute spec is set - if (spec == null || spec.length() == 0) - { - log("spec missing!", Project.MSG_ERR); - } - //check if required attribute specsrc is set - else if (specdir == null || specdir.length() == 0) - { - log("specsrc is missing!", Project.MSG_ERR); - } - //check if required attribute dest is set - else if (destdir == null || destdir.length() == 0) - { - log("dest is missing!", Project.MSG_ERR); - } - //check if required classpath is set - else if (classpath == null) - { - log("classpath is missing!", Project.MSG_ERR); - } - else - { - //check optional attribute classpath - if (classpath == null) - log("No classpath set! Is this correct?", Project.MSG_WARN); - - //if optional attribute cup2src is set, compile cup2 sources - if (cup2srcdir != null) - { - this.compileCup2(); - } - - //compiling the CUPSpecification - compileSpec(); - if (verbose) - log(" === Compiling Spec done ==="); - - //instantiate CUPSpecification - try - { - this.cup2Specification = this.createCUP2Specification(); - } - catch (InstantiationException e) - { - log("InstantiationException when trying to create CUP2Specification"); - e.printStackTrace(); - } - catch (IllegalSpecException e) - { - log("CUP2 Specification invalid : " + e); - e.printStackTrace(); - } - catch (ClassNotFoundException e) - { - log("ClassNotFoundException when trying to create CUP2Specification"); - e.printStackTrace(); - } - catch (IllegalAccessException e) - { - log("IllegalAccessException when trying to create CUP2Specification"); - e.printStackTrace(); - } - - if (this.cup2Specification == null) - { - log("Error: could not create CUPSpecification", Project.MSG_ERR); - } - else - { - //Productivity and reachability is checked - if (verbose) - log(" === Checking productivity and reachability ==="); - - IGrammar orgGram = this.cup2Specification.getGrammar(); - CheckedGrammar cg = new CheckedGrammar(orgGram); - if (!cg.isReduced()) - { - log("The grammar is not reduced!", Project.MSG_WARN); - if (cg.hasNotProductiveNonTerminals()) - { - log("The grammar contains not productive NonTerminals!", Project.MSG_WARN); - LinkedList notProductiveNonTerminals = cg - .getNotProductiveNonTerminals(); - log("Not productive are: " + notProductiveNonTerminals.toString()); - - } - if (cg.hasNotReachableNonTerminals()) - { - log("The grammar contains not reachable NonTerminals!", Project.MSG_WARN); - LinkedList notReachableNonTerminals = cg - .getNotReachableNonTerminals(); - log("Not reachable are: " + notReachableNonTerminals.toString()); - - } - } - - //create serialization - LRParsingTable table = null; - try - { - if (verbose) - log(" === Creating parsing table ==="); - - //create Parsing Table - LRGenerator generator = createGenerator(this.cup2Specification); - table = generator.getParsingTable(); - - LRParser parser = new LRParser(table); - if (verbose) - log(" === Serializing parser ==="); - serializeParser(parser); - } - catch (ShiftReduceConflict e) - { - log("Shift-Reduce-Conflict during parser generation: " + e.getMessage(), - Project.MSG_ERR); - } - catch (ReduceReduceConflict e) - { - log("Reduc-Reduce-Conflict during parser generation: " + e.getMessage(), - Project.MSG_ERR); - } - catch (GeneratorException e) - { - log("GeneratorException occured during parser generation: " + e.getMessage(), - Project.MSG_ERR); - } - catch (IllegalSpecException e) - { - log("Cup2 Specification invalid : " + e); - e.printStackTrace(); - } - - //print parsing table - if (this.parsetable != null && parsetable.length() > 0) - { - printParsingTable(table, this.parsetable); - } - } - } /*end else*/ - if (verbose) - log(" === Cup2 Ant Task ended ===", Project.MSG_INFO); - } /*end of execute*/ - - - /** - * display all field values - */ - private void displayValues() - { - log("cup2 task - attribute settings: \n ========================", Project.MSG_INFO); - log("spec..........: " + this.spec); - log("- source......: " + getSpecAbsolutePath()); - log("- serialized..: " + getSerializedSpecAbsolutePath()); - log("specdir.......: " + this.specdir); - log("cup2srcdir....: " + this.cup2srcdir); - log("classpath.....: " + this.classpath); - log("algorithm.....: " + this.algorithm); - log("verbose.......: " + this.verbose); - log("parsetable....: " + this.parsetable); - log(" ========================"); - } - - - /** - * Compiles CUP2 from its sources. - */ - private void compileCup2() - { - log(" === Compiling CUP2 sources ===", Project.MSG_INFO); - - //set params - Javac jcct = new Javac(); - jcct.setSourcepath(new Path(this.getProject(), cup2srcdir)); - jcct.setSrcdir(new Path(this.getProject(), cup2srcdir)); - jcct.setDestdir(new File(this.destdir)); - jcct.setClasspath(classpath); - - jcct.setProject(this.getProject()); - - //compile cup2 sources - jcct.execute(); - } - - - /** - * Compiles the CUP2Specification. - */ - private void compileSpec() - { - //setting params for the compile task - log(" === Creating parser (Specification = " + this.spec + " Directory = " - + this.specdir + ")", Project.MSG_INFO); - Javac jsct = new Javac(); //JavaSpecCompileTaks - jsct.setProject(this.getProject()); - jsct.setSrcdir(new Path(this.getProject(), specdir)); - jsct.setIncludes(getSpecRelativePath()); - jsct.setClasspath(classpath); - jsct.setDestdir(new File(destdir)); - - //compile the spec - jsct.execute(); - } - - - /** - * Creates a generator depending on algorithm attribute. - */ - private LRGenerator createGenerator( - CUP2Specification spec) - throws GeneratorException - { - LRGenerator generator = null; - if (verbose) - log("algorithm = " + algorithm); - if (this.algorithm == null || this.algorithm.equals("lr1")) - { - if (verbose) - log("instantiating lr1 generator..."); - generator = new LR1Generator(spec); - } - else if (this.algorithm.equals("lr0")) - { - if (verbose) - log("instantiating lr0 generator..."); - generator = new LR0Generator(spec); - } - else if (this.algorithm.equals("lalr1")) - { - if (verbose) - log("instantiating lalr1 generator..."); - generator = new LALR1Generator(spec); - } - return generator; - } - - - /** - * Instantiates the {@link CUP2Specification} with reflection. - */ - @SuppressWarnings("unchecked") private CUP2Specification createCUP2Specification() - throws ClassNotFoundException, IllegalAccessException, InstantiationException - { - Class c = (Class) Class.forName(spec); - return (CUP2Specification) c.newInstance(); - } - - - /** - * Saves the parsing table to a file - * (if the related specification is newer than the - * cup2 file that is to hold the serialization) - */ - private void serializeParser(LRParser parser) - { - File cup2File = new File(getSerializedSpecAbsolutePath()); - File specFile = new File(getSpecAbsolutePath()); - - if (verbose) //if verbose = "on" ==> print files and time stamps - { - log("specification source file = " + getSpecAbsolutePath()); - log("Cup2 file for serialization = " + cup2File.getAbsolutePath()); - log("specification - last modified = " + specFile.lastModified()); - log("CUP2 file - last modified = " + cup2File.lastModified()); - } - - //check if CUP2 file is not older than spec (spec has to be newer) - if (specFile.lastModified() > cup2File.lastModified() || !cup2File.exists()) - { - //spec file is newer ==> serialize - - if (verbose && !cup2File.exists()) - log("cup2 file for serialization does not yet exist and is created..."); - else if (verbose && specFile.lastModified() > cup2File.lastModified()) - log("Spec file is newer than cup2 file... creating cup2 file"); - - //create LRParserSerialization object - LRParserSerialization serial = new LRParserSerialization(cup2File.getAbsolutePath()); - - //serialize parser - serial.saveParser(parser); - } - else if (verbose) - { - log("Specification is older than CUP2 file. No serialization."); - } - } - - - /** - * output the parsing table of the LRParser to a file - * @param table: Parsing Table of an LRParser - * @param fileName: filename for the output - */ - private void printParsingTable(LRParsingTable table, String fileName) - { - if (this.verbose) - { - log("Printing ParsingTable to: " + fileName); - } - File file = new File(fileName); - LRParsingTableDump.dumpToHTML(table, file); - } - - - private String getSpecRelativePath() - { - return spec.replaceAll("\\.", "/") + ".java"; - } - - - private String getSpecAbsolutePath() - { - return specdir + "/" + getSpecRelativePath(); - } - - - private String getSerializedSpecAbsolutePath() - { - return destdir + "/" + spec.replaceAll("\\.", "/") + ".cup2"; - } - - - public void setSpec(String spec) - { - this.spec = spec; - } - - - public void setSpecdir(String specdir) - { - this.specdir = specdir; - } - - - public void setDestdir(String destdir) - { - this.destdir = destdir; - } - - - public void addConfiguredClasspath(Path cp) - { - this.classpath = cp; - } - - - public void setCup2srcdir(String cup2srcdir) - { - this.cup2srcdir = cup2srcdir; - } - - - public void setAlgorithm(String s) - { - this.algorithm = s.trim().toLowerCase(); - } - - - public void setVerbose(boolean b) - { - this.verbose = b; - } - - - public void setParsetable(String parsetable) - { - this.parsetable = parsetable; - } - - +public class CUP2AntTask extends Task { + + // required attributes + private String spec = null; // specification name; e.g: edu.tum.cup2.test.SpecCalc4 + private String specdir = null; // root source directory for the specification + private String destdir = null; // destination directory for CUP2 and the compiled spec + private Path classpath = null; + + // optional attributes + private String cup2srcdir = null; // source path of cup2 + private String algorithm = null; // generator algorithm; e.g: lr1 + private boolean verbose = false; // print a lot of status messages? + private String parsetable = null; // where to store the parsing table? + + // other fields + private CUP2Specification cup2Specification = null; // instantiated specification + + /** executing the cup2 task */ + public void execute() { + // print parameter settings + if (this.verbose) this.displayValues(); + + // check if required attribute spec is set + if (spec == null || spec.length() == 0) { + log("spec missing!", Project.MSG_ERR); + } + // check if required attribute specsrc is set + else if (specdir == null || specdir.length() == 0) { + log("specsrc is missing!", Project.MSG_ERR); + } + // check if required attribute dest is set + else if (destdir == null || destdir.length() == 0) { + log("dest is missing!", Project.MSG_ERR); + } + // check if required classpath is set + else if (classpath == null) { + log("classpath is missing!", Project.MSG_ERR); + } else { + // check optional attribute classpath + if (classpath == null) log("No classpath set! Is this correct?", Project.MSG_WARN); + + // if optional attribute cup2src is set, compile cup2 sources + if (cup2srcdir != null) { + this.compileCup2(); + } + + // compiling the CUPSpecification + compileSpec(); + if (verbose) log(" === Compiling Spec done ==="); + + // instantiate CUPSpecification + try { + this.cup2Specification = this.createCUP2Specification(); + } catch (InstantiationException e) { + log("InstantiationException when trying to create CUP2Specification"); + e.printStackTrace(); + } catch (IllegalSpecException e) { + log("CUP2 Specification invalid : " + e); + e.printStackTrace(); + } catch (ClassNotFoundException e) { + log("ClassNotFoundException when trying to create CUP2Specification"); + e.printStackTrace(); + } catch (IllegalAccessException e) { + log("IllegalAccessException when trying to create CUP2Specification"); + e.printStackTrace(); + } + + if (this.cup2Specification == null) { + log("Error: could not create CUPSpecification", Project.MSG_ERR); + } else { + // Productivity and reachability is checked + if (verbose) log(" === Checking productivity and reachability ==="); + + IGrammar orgGram = this.cup2Specification.getGrammar(); + CheckedGrammar cg = new CheckedGrammar(orgGram); + if (!cg.isReduced()) { + log("The grammar is not reduced!", Project.MSG_WARN); + if (cg.hasNotProductiveNonTerminals()) { + log("The grammar contains not productive NonTerminals!", Project.MSG_WARN); + LinkedList notProductiveNonTerminals = cg.getNotProductiveNonTerminals(); + log("Not productive are: " + notProductiveNonTerminals.toString()); + } + if (cg.hasNotReachableNonTerminals()) { + log("The grammar contains not reachable NonTerminals!", Project.MSG_WARN); + LinkedList notReachableNonTerminals = cg.getNotReachableNonTerminals(); + log("Not reachable are: " + notReachableNonTerminals.toString()); + } + } + + // create serialization + LRParsingTable table = null; + try { + if (verbose) log(" === Creating parsing table ==="); + + // create Parsing Table + LRGenerator generator = createGenerator(this.cup2Specification); + table = generator.getParsingTable(); + + LRParser parser = new LRParser(table); + if (verbose) log(" === Serializing parser ==="); + serializeParser(parser); + } catch (ShiftReduceConflict e) { + log("Shift-Reduce-Conflict during parser generation: " + e.getMessage(), Project.MSG_ERR); + } catch (ReduceReduceConflict e) { + log("Reduc-Reduce-Conflict during parser generation: " + e.getMessage(), Project.MSG_ERR); + } catch (GeneratorException e) { + log( + "GeneratorException occured during parser generation: " + e.getMessage(), + Project.MSG_ERR); + } catch (IllegalSpecException e) { + log("Cup2 Specification invalid : " + e); + e.printStackTrace(); + } + + // print parsing table + if (this.parsetable != null && parsetable.length() > 0) { + printParsingTable(table, this.parsetable); + } + } + } /*end else*/ + if (verbose) log(" === Cup2 Ant Task ended ===", Project.MSG_INFO); + } /*end of execute*/ + + /** display all field values */ + private void displayValues() { + log("cup2 task - attribute settings: \n ========================", Project.MSG_INFO); + log("spec..........: " + this.spec); + log("- source......: " + getSpecAbsolutePath()); + log("- serialized..: " + getSerializedSpecAbsolutePath()); + log("specdir.......: " + this.specdir); + log("cup2srcdir....: " + this.cup2srcdir); + log("classpath.....: " + this.classpath); + log("algorithm.....: " + this.algorithm); + log("verbose.......: " + this.verbose); + log("parsetable....: " + this.parsetable); + log(" ========================"); + } + + /** Compiles CUP2 from its sources. */ + private void compileCup2() { + log(" === Compiling CUP2 sources ===", Project.MSG_INFO); + + // set params + Javac jcct = new Javac(); + jcct.setSourcepath(new Path(this.getProject(), cup2srcdir)); + jcct.setSrcdir(new Path(this.getProject(), cup2srcdir)); + jcct.setDestdir(new File(this.destdir)); + jcct.setClasspath(classpath); + + jcct.setProject(this.getProject()); + + // compile cup2 sources + jcct.execute(); + } + + /** Compiles the CUP2Specification. */ + private void compileSpec() { + // setting params for the compile task + log( + " === Creating parser (Specification = " + + this.spec + + " Directory = " + + this.specdir + + ")", + Project.MSG_INFO); + Javac jsct = new Javac(); // JavaSpecCompileTaks + jsct.setProject(this.getProject()); + jsct.setSrcdir(new Path(this.getProject(), specdir)); + jsct.setIncludes(getSpecRelativePath()); + jsct.setClasspath(classpath); + jsct.setDestdir(new File(destdir)); + + // compile the spec + jsct.execute(); + } + + /** Creates a generator depending on algorithm attribute. */ + private LRGenerator createGenerator(CUP2Specification spec) + throws GeneratorException { + LRGenerator generator = null; + if (verbose) log("algorithm = " + algorithm); + if (this.algorithm == null || Objects.equals(this.algorithm, "lr1")) { + if (verbose) log("instantiating lr1 generator..."); + generator = new LR1Generator(spec); + } else if (Objects.equals(this.algorithm, "lr0")) { + if (verbose) log("instantiating lr0 generator..."); + generator = new LR0Generator(spec); + } else if (Objects.equals(this.algorithm, "lalr1")) { + if (verbose) log("instantiating lalr1 generator..."); + generator = new LALR1Generator(spec); + } + return generator; + } + + /** Instantiates the {@link CUP2Specification} with reflection. */ + @SuppressWarnings("unchecked") + private CUP2Specification createCUP2Specification() + throws ClassNotFoundException, IllegalAccessException, InstantiationException { + Class c = (Class) Class.forName(spec); + return (CUP2Specification) c.newInstance(); + } + + /** + * Saves the parsing table to a file (if the related specification is newer than the cup2 file + * that is to hold the serialization) + */ + private void serializeParser(LRParser parser) { + File cup2File = new File(getSerializedSpecAbsolutePath()); + File specFile = new File(getSpecAbsolutePath()); + + if (verbose) // if verbose = "on" ==> print files and time stamps + { + log("specification source file = " + getSpecAbsolutePath()); + log("Cup2 file for serialization = " + cup2File.getAbsolutePath()); + log("specification - last modified = " + specFile.lastModified()); + log("CUP2 file - last modified = " + cup2File.lastModified()); + } + + // check if CUP2 file is not older than spec (spec has to be newer) + if (specFile.lastModified() > cup2File.lastModified() || !cup2File.exists()) { + // spec file is newer ==> serialize + + if (verbose && !cup2File.exists()) + log("cup2 file for serialization does not yet exist and is created..."); + else if (verbose && specFile.lastModified() > cup2File.lastModified()) + log("Spec file is newer than cup2 file... creating cup2 file"); + + // create LRParserSerialization object + LRParserSerialization serial = new LRParserSerialization(cup2File.getAbsolutePath()); + + // serialize parser + serial.saveParser(parser); + } else if (verbose) { + log("Specification is older than CUP2 file. No serialization."); + } + } + + /** + * output the parsing table of the LRParser to a file + * + * @param table: Parsing Table of an LRParser + * @param fileName: filename for the output + */ + private void printParsingTable(LRParsingTable table, String fileName) { + if (this.verbose) { + log("Printing ParsingTable to: " + fileName); + } + File file = new File(fileName); + LRParsingTableDump.dumpToHTML(table, file); + } + + private String getSpecRelativePath() { + return spec.replaceAll("\\.", "/") + ".java"; + } + + private String getSpecAbsolutePath() { + return specdir + "/" + getSpecRelativePath(); + } + + private String getSerializedSpecAbsolutePath() { + return destdir + "/" + spec.replaceAll("\\.", "/") + ".cup2"; + } + + public void setSpec(String spec) { + this.spec = spec; + } + + public void setSpecdir(String specdir) { + this.specdir = specdir; + } + + public void setDestdir(String destdir) { + this.destdir = destdir; + } + + public void addConfiguredClasspath(Path cp) { + this.classpath = cp; + } + + public void setCup2srcdir(String cup2srcdir) { + this.cup2srcdir = cup2srcdir; + } + + public void setAlgorithm(String s) { + this.algorithm = s.trim().toLowerCase(); + } + + public void setVerbose(boolean b) { + this.verbose = b; + } + + public void setParsetable(String parsetable) { + this.parsetable = parsetable; + } } diff --git a/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/generator/Automaton.java b/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/generator/Automaton.java index 4f803d371..7baf02f72 100644 --- a/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/generator/Automaton.java +++ b/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/generator/Automaton.java @@ -1,129 +1,100 @@ package edu.tum.cup2.generator; +import edu.tum.cup2.generator.items.Item; +import edu.tum.cup2.generator.states.State; +import edu.tum.cup2.io.IAutomatonVisitor; +import edu.tum.cup2.io.IVisitedElement; import java.util.Collections; import java.util.Comparator; import java.util.HashSet; import java.util.LinkedList; import java.util.List; +import java.util.Objects; import java.util.Set; -import edu.tum.cup2.generator.items.Item; -import edu.tum.cup2.generator.states.State; -import edu.tum.cup2.io.IAutomatonVisitor; -import edu.tum.cup2.io.IVisitedElement; - - /** - * An automaton consists of states and edges - * connecting the states. - * + * An automaton consists of states and edges connecting the states. + * * @author Andreas Wenger */ -public class Automaton> implements IVisitedElement -{ - - private Set states = Collections.synchronizedSet(new HashSet()); - private Set edges = Collections.synchronizedSet(new HashSet()); - private S startState = null; - - - public Automaton(S startState) - { - states.add(startState); - this.startState = startState; - } - - - public Set getStates() - { - return states; - } - - - public Set getEdges() - { - return edges; - } - - - public S getStartState() - { - return startState; - } - - - public void visited(IAutomatonVisitor visitor) - { - visitor.visit(this); - } - - - /** - * Gets all edges starting at the given state. - * This implementation is not optimized and runs in O(n). - */ - public LinkedList getEdgesFrom(S state) - { - LinkedList ret = new LinkedList(); - for (Edge edge : edges) - { - if (edge.getSrc().equals(state)) - ret.add(edge); - } - return ret; - } - - - /** - * Gets the edge arriving in the given state. - * This implementation is not optimized and runs in O(n). - */ - public List getEdgeTo(State state) - { - LinkedList ret = new LinkedList(); - for (Edge edge : edges) - { - State dest = edge.getDest(); - if (dest != null && dest.equals(state)) - ret.add(edge); - } - return ret; - } - - - @SuppressWarnings("unchecked") - @Override - public String toString() - { - Comparator comp = new Comparator() { - public int compare(Edge o1, Edge o2) - { - return o1.toString().compareTo(o2.toString()); - } - }; - StringBuffer sb = new StringBuffer(); - LinkedList queue = new LinkedList(); - HashSet set = new HashSet(); - queue.add(getStartState()); - while (!queue.isEmpty()) - { - S s = queue.poll(); - sb.append(s.toString()); - set.add(s); - List sortedList = getEdgesFrom(s); - Collections.sort(sortedList, comp); - for (Edge e : sortedList) - { - if (set.contains(e.getDest())) - continue; - State st = e.getDest(); - if (st != null) - queue.add((S) st); - - } - } - - return sb.toString();// +"\n"+edges.toString(); - } - +public class Automaton> implements IVisitedElement { + + private Set states = Collections.synchronizedSet(new HashSet()); + private Set edges = Collections.synchronizedSet(new HashSet()); + private S startState = null; + + public Automaton(S startState) { + states.add(startState); + this.startState = startState; + } + + public Set getStates() { + return states; + } + + public Set getEdges() { + return edges; + } + + public S getStartState() { + return startState; + } + + public void visited(IAutomatonVisitor visitor) { + visitor.visit(this); + } + + /** + * Gets all edges starting at the given state. This implementation is not optimized and runs in + * O(n). + */ + public LinkedList getEdgesFrom(S state) { + LinkedList ret = new LinkedList(); + for (Edge edge : edges) { + if (Objects.equals(edge.getSrc(), state)) ret.add(edge); + } + return ret; + } + + /** + * Gets the edge arriving in the given state. This implementation is not optimized and runs in + * O(n). + */ + public List getEdgeTo(State state) { + LinkedList ret = new LinkedList(); + for (Edge edge : edges) { + State dest = edge.getDest(); + if (dest != null && Objects.equals(dest, state)) ret.add(edge); + } + return ret; + } + + @SuppressWarnings("unchecked") + @Override + public String toString() { + Comparator comp = + new Comparator() { + public int compare(Edge o1, Edge o2) { + return o1.toString().compareTo(o2.toString()); + } + }; + StringBuffer sb = new StringBuffer(); + LinkedList queue = new LinkedList(); + HashSet set = new HashSet(); + queue.add(getStartState()); + while (!queue.isEmpty()) { + S s = queue.poll(); + sb.append(s.toString()); + set.add(s); + List sortedList = getEdgesFrom(s); + Collections.sort(sortedList, comp); + for (Edge e : sortedList) { + if (set.contains(e.getDest())) continue; + State st = e.getDest(); + if (st != null) queue.add((S) st); + } + } + + return sb.toString(); // +"\n"+edges.toString(); + } } diff --git a/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/generator/Edge.java b/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/generator/Edge.java index 82f288728..0a3237ec2 100644 --- a/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/generator/Edge.java +++ b/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/generator/Edge.java @@ -6,117 +6,86 @@ import edu.tum.cup2.grammar.Symbol; import edu.tum.cup2.io.IAutomatonVisitor; import edu.tum.cup2.io.IVisitedElement; - +import java.util.Objects; /** * An edge within a LR state transition diagram. - * - * An edge is identified by the source {@link State} and a - * {@link Symbol}, since it is a deterministic finite automata. - * The destination {@link State} is also saved, because the - * edges are needed for constructing the parsing table later. - * - * The {@link Item} of the source {@link State} is saved too, - * because we need the corresponding production and its position - * for assigning semantic actions to shift operations later. - * + * + *

An edge is identified by the source {@link State} and a {@link Symbol}, since it is a + * deterministic finite automata. The destination {@link State} is also saved, because the edges are + * needed for constructing the parsing table later. + * + *

The {@link Item} of the source {@link State} is saved too, because we need the corresponding + * production and its position for assigning semantic actions to shift operations later. + * * @author Andreas Wenger */ -public final class Edge - implements IVisitedElement -{ +public final class Edge implements IVisitedElement { + + private final State src, dest; + private final Symbol symbol; + private final LR0Item srcItem; + private final int hashCache; + + /** Edge which leads to another non-accepting state. */ + public Edge(State src, Symbol symbol, State dest, LR0Item srcItem) { + this.src = src; + this.symbol = symbol; + this.dest = dest; + this.srcItem = srcItem; + this.hashCache = src.hashCode() * 100 + symbol.hashCode(); + } + + /** Edge which leads to the accepting state. */ + public static Edge createAcceptEdge(State src, Symbol symbol) { + return new Edge(src, symbol, null, null); + } - private final State src, dest; - private final Symbol symbol; - private final LR0Item srcItem; - private final int hashCache; - - - /** - * Edge which leads to another non-accepting state. - */ - public Edge(State src, Symbol symbol, State dest, LR0Item srcItem) - { - this.src = src; - this.symbol = symbol; - this.dest = dest; - this.srcItem = srcItem; - this.hashCache = src.hashCode() * 100 + symbol.hashCode(); - } - - - /** - * Edge which leads to the accepting state. - */ - public static Edge createAcceptEdge(State src, Symbol symbol) - { - return new Edge(src, symbol, null, null); - } - - - public State getSrc() - { - return src; - } + public State getSrc() { + return src; + } - - public State getDest() - { - return dest; - } + public State getDest() { + return dest; + } - - public Symbol getSymbol() - { - return symbol; - } - - - public LR0Item getSrcItem() - { - return srcItem; - } - - - public boolean isDestAccepting() - { - return dest == null; - } - - - /** - * Returns true, if the given object is an edge and if it starts from - * the same state using the same symbol. - * The destination state and the source item are not checked! - */ - @Override public boolean equals(Object obj) - { - if (obj instanceof Edge) - { - Edge e = (Edge) obj; - return src.equals(e.src) && symbol.equals(e.symbol); - } - return false; - } - - - @Override public int hashCode() - { - return hashCache; - } + public Symbol getSymbol() { + return symbol; + } - /** - * method to accept a visitor (also see "visitor pattern") - */ - public void visited(IAutomatonVisitor visitor) - { - visitor.visit(this); - } + public LR0Item getSrcItem() { + return srcItem; + } - @Override - public String toString() { - return srcItem + " "+symbol; + public boolean isDestAccepting() { + return dest == null; + } + + /** + * Returns true, if the given object is an edge and if it starts from the same state using the + * same symbol. The destination state and the source item are not checked! + */ + @Override + public boolean equals(Object obj) { + if (obj instanceof Edge) { + Edge e = (Edge) obj; + return Objects.equals(src, e.src) && Objects.equals(symbol, e.symbol); } - - + return false; + } + + @Override + public int hashCode() { + return hashCache; + } + + /** method to accept a visitor (also see "visitor pattern") */ + public void visited(IAutomatonVisitor visitor) { + visitor.visit(this); + } + + @Override + public String toString() { + return srcItem + " " + symbol; + } } diff --git a/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/generator/FirstSets.java b/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/generator/FirstSets.java index df86e5831..db8f4af1f 100644 --- a/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/generator/FirstSets.java +++ b/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/generator/FirstSets.java @@ -3,131 +3,104 @@ import static edu.tum.cup2.grammar.SpecialTerminals.Epsilon; import static edu.tum.cup2.util.CollectionTools.map; -import java.util.Map; - import edu.tum.cup2.generator.terminals.EfficientTerminalSet; import edu.tum.cup2.grammar.Grammar; import edu.tum.cup2.grammar.NonTerminal; import edu.tum.cup2.grammar.Production; import edu.tum.cup2.grammar.Symbol; import edu.tum.cup2.grammar.Terminal; - +import java.util.Map; +import java.util.Objects; /** - * The FIRST sets of all symbols. - * This is the set of all terminals that may appear at the beginning + * The FIRST sets of all symbols. This is the set of all terminals that may appear at the beginning * of a string derived from the given symbol. - * + * * @author Andreas Wenger */ -public final class FirstSets -{ - - private final Map data; - - - - /** - * Computes the FIRST sets of all symbols of the - * given grammar, using the precomputed set of nullable non-terminals. - * This algorithm is inspired by ASU99, page 230 (german edition) (rules) - * and Appel's book, page 51 (rule 3 implementation). - */ - public FirstSets(Grammar grammar, NullableSet nullableSet) - { - Map data = map(); - - //initialize map for all symbols - EfficientTerminalSet emptySet = new EfficientTerminalSet(grammar.getTerminals()); - for (Terminal terminal : grammar.getTerminals()) - { - data.put(terminal, emptySet); - } - for (NonTerminal nonTerminal : grammar.getNonTerminals()) - { - data.put(nonTerminal, emptySet); - } - - //rule 1: if X is a terminal, then FIRST(X) = {X} - for (Terminal terminal : grammar.getTerminals()) - { - data.put(terminal, data.get(terminal).plus(terminal)); - } - data.put(Epsilon, emptySet.plus(Epsilon)); - - //rule 2: if X → ɛ is a production, add ɛ to FIRST(X) - for (NonTerminal nullableNonTerminal : nullableSet) - { - data.put(nullableNonTerminal, data.get(nullableNonTerminal).plus(Epsilon)); - } - - //rule 3: if X is a non-terminal and X → Y1 Y2 ... Yk is - //a production, then add a to FIRST(X), if a is in FIRST(Yi) for - //any i and FIRST(Y1) ... FIRST(Yi-1) are all nullable. if all - //FIRST(Y1) ... FIRST(Yk) are nullable, add ɛ to FIRST(X) - //(Appel ignores the epsilon, but we do not, according to ASU99) - boolean changed; - do - { - changed = false; - for (Production production : grammar.getProductions()) - { - NonTerminal lhs = production.getLHS(); - //find first not nullable rhs symbol. up to this - //symbol, add all FIRST-symbols of these rhs symbols to - //the FIRST-set of the current lhs symbol - Symbol firstNotNullableSymbol = null; - for (Symbol symbol : production.getRHS()) - { - if (!(symbol == Epsilon || nullableSet.contains(symbol))) - { - //terminal or not nullable non-terminal, stop - firstNotNullableSymbol = symbol; - break; - } - else - { - //epsilon or nullable non-terminal, so go on, but - //add their FIRST values - EfficientTerminalSet to = data.get(lhs); - EfficientTerminalSet from = data.get(symbol); - EfficientTerminalSet merged = to.plusAllExceptEpsilon(from); - data.put(lhs, merged); - changed |= (!merged.equals(to)); - } - } - //first not nullable symbol found? than add its FIRST values. - //otherwise add epsilon - if (firstNotNullableSymbol != null) - { - EfficientTerminalSet to = data.get(lhs); - EfficientTerminalSet from = data.get(firstNotNullableSymbol); - EfficientTerminalSet merged = to.plusAllExceptEpsilon(from); - data.put(lhs, merged); - changed |= (!merged.equals(to)); - } - else - { - EfficientTerminalSet before = data.get(lhs); - EfficientTerminalSet after = before.plus(Epsilon); - data.put(lhs, after); - changed |= (!before.equals(after)); - } - } - } - while (changed); - - this.data = data; - } - - - /** - * Gets the FIRST set for the given symbol. - */ - public EfficientTerminalSet get(Symbol symbol) - { - return data.get(symbol); - } - +public final class FirstSets { + + private final Map data; + + /** + * Computes the FIRST sets of all symbols of the given grammar, using the precomputed set of + * nullable non-terminals. This algorithm is inspired by ASU99, page 230 (german edition) (rules) + * and Appel's book, page 51 (rule 3 implementation). + */ + public FirstSets(Grammar grammar, NullableSet nullableSet) { + Map data = map(); + + // initialize map for all symbols + EfficientTerminalSet emptySet = new EfficientTerminalSet(grammar.getTerminals()); + for (Terminal terminal : grammar.getTerminals()) { + data.put(terminal, emptySet); + } + for (NonTerminal nonTerminal : grammar.getNonTerminals()) { + data.put(nonTerminal, emptySet); + } + + // rule 1: if X is a terminal, then FIRST(X) = {X} + for (Terminal terminal : grammar.getTerminals()) { + data.put(terminal, data.get(terminal).plus(terminal)); + } + data.put(Epsilon, emptySet.plus(Epsilon)); + + // rule 2: if X → ɛ is a production, add ɛ to FIRST(X) + for (NonTerminal nullableNonTerminal : nullableSet) { + data.put(nullableNonTerminal, data.get(nullableNonTerminal).plus(Epsilon)); + } + + // rule 3: if X is a non-terminal and X → Y1 Y2 ... Yk is + // a production, then add a to FIRST(X), if a is in FIRST(Yi) for + // any i and FIRST(Y1) ... FIRST(Yi-1) are all nullable. if all + // FIRST(Y1) ... FIRST(Yk) are nullable, add ɛ to FIRST(X) + // (Appel ignores the epsilon, but we do not, according to ASU99) + boolean changed; + do { + changed = false; + for (Production production : grammar.getProductions()) { + NonTerminal lhs = production.getLHS(); + // find first not nullable rhs symbol. up to this + // symbol, add all FIRST-symbols of these rhs symbols to + // the FIRST-set of the current lhs symbol + Symbol firstNotNullableSymbol = null; + for (Symbol symbol : production.getRHS()) { + if (!(symbol == Epsilon || nullableSet.contains(symbol))) { + // terminal or not nullable non-terminal, stop + firstNotNullableSymbol = symbol; + break; + } else { + // epsilon or nullable non-terminal, so go on, but + // add their FIRST values + EfficientTerminalSet to = data.get(lhs); + EfficientTerminalSet from = data.get(symbol); + EfficientTerminalSet merged = to.plusAllExceptEpsilon(from); + data.put(lhs, merged); + changed |= (!Objects.equals(merged, to)); + } + } + // first not nullable symbol found? than add its FIRST values. + // otherwise add epsilon + if (firstNotNullableSymbol != null) { + EfficientTerminalSet to = data.get(lhs); + EfficientTerminalSet from = data.get(firstNotNullableSymbol); + EfficientTerminalSet merged = to.plusAllExceptEpsilon(from); + data.put(lhs, merged); + changed |= (!Objects.equals(merged, to)); + } else { + EfficientTerminalSet before = data.get(lhs); + EfficientTerminalSet after = before.plus(Epsilon); + data.put(lhs, after); + changed |= (!Objects.equals(before, after)); + } + } + } while (changed); + + this.data = data; + } + /** Gets the FIRST set for the given symbol. */ + public EfficientTerminalSet get(Symbol symbol) { + return data.get(symbol); + } } diff --git a/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/generator/FollowSets.java b/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/generator/FollowSets.java index 5b17cc3e3..27d81f9e7 100644 --- a/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/generator/FollowSets.java +++ b/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/generator/FollowSets.java @@ -1,151 +1,136 @@ package edu.tum.cup2.generator; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; - import edu.tum.cup2.generator.terminals.EfficientTerminalSet; import edu.tum.cup2.grammar.Grammar; import edu.tum.cup2.grammar.NonTerminal; import edu.tum.cup2.grammar.Production; import edu.tum.cup2.grammar.SpecialTerminals; import edu.tum.cup2.grammar.Symbol; - +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Objects; /** - * Computes the FOLLOW sets of all non-terminals of the given grammar, using the precomputed set of nullable - * non-terminals and first sets. This algorithm is inspired by ASU99, page 230 (german edition) (rules). - * + * Computes the FOLLOW sets of all non-terminals of the given grammar, using the precomputed set of + * nullable non-terminals and first sets. This algorithm is inspired by ASU99, page 230 (german + * edition) (rules). + * * @author Gero */ -public class FollowSets -{ - private final Map data; - - - /** - * @param grammar - * @param firstSets - */ - public FollowSets(Grammar grammar, FirstSets firstSets) - { - data = new HashMap(); - - // Initialize data map - final EfficientTerminalSet emptySet = new EfficientTerminalSet(grammar.getTerminals()); - for (NonTerminal nonTerminal : grammar.getNonTerminals()) - { - data.put(nonTerminal, emptySet); // Viable because all modifiying operations will create new instances - } - - // Rule 1: For start symbol S, add $ to FOLLOW(S) - final NonTerminal startSymbol = grammar.getStartProduction().getLHS(); - final EfficientTerminalSet startSet = emptySet.plus(SpecialTerminals.EndOfInputStream); - data.put(startSymbol, startSet); - - - // Actual algorithm: Apply rule 2 and 3 until nothing changes anymore - boolean changed; - do - { - changed = false; - - for (Production production : grammar.getProductions()) - { - // Rule 2: For a production A → αBβ, every element of FIRST(β) (w/o ɛ) is an element to FOLLOW(B) - // Rule 3: For productions A → αB (3.1) or A → αBβ with FIRST(β) containing ɛ (3.2), every element of - // FOLLOW(A) is also an element of FOLLOW(B) - - - // ### Find B (A → αBβ / A → αB => first non-terminal in rhs) ... - final List rhs = production.getRHS(); - - NonTerminal nonTerminalB = null; - Symbol nextSymbol = null; - - final Iterator rhsIt = rhs.iterator(); - for (; rhsIt.hasNext();) - { - Symbol symbol = rhsIt.next(); - if (symbol instanceof NonTerminal) - { - nonTerminalB = (NonTerminal) symbol; - - // Great, found non-terminal B! Check for β... - if (rhsIt.hasNext()) - { - nextSymbol = rhsIt.next(); - } - - break; - } - } - - if (nonTerminalB == null) - { - // If there's no non-terminal: continue with next production - continue; - } - - - // ### Check for rule 2 and 3 - final boolean notLastSymbol = !(nextSymbol == null); - final NonTerminal nonTerminalA = production.getLHS(); - final EfficientTerminalSet followB = data.get(nonTerminalB); - - // Differ between αBβ and αB ... - if (notLastSymbol) - { - // Identified A → αBβ => Apply rule 2: Every element of FIRST(β) (w/o ɛ) is added to FOLLOW(B) - // Important: β may be a list of Symbols! If a first set for a symbol contains ɛ, the following must be considered, to! - final Symbol beta = nextSymbol; - EfficientTerminalSet firstBeta = firstSets.get(beta); - - // Aggregate first sets if all sets before contained ɛ - while (rhsIt.hasNext() && firstBeta.contains(SpecialTerminals.Epsilon)) - { - Symbol symbol = rhsIt.next(); - final EfficientTerminalSet firstNextBeta = firstSets.get(symbol); - firstBeta = firstBeta.plusAll(firstNextBeta); - } - EfficientTerminalSet newFollowB = followB.plusAllExceptEpsilon(firstBeta); - - - // Check for condition 3.2: FIRST(β) contains ɛ - if (firstBeta.contains(SpecialTerminals.Epsilon)) - { - // Apply rule 3: Every element of FOLLOW(A) is also an element of FOLLOW(B) - final EfficientTerminalSet followA = data.get(nonTerminalA); - newFollowB = newFollowB.plusAll(followA); - } - - - // Finish: put new FOLLOW(B) into data map and check for changes - data.put(nonTerminalB, newFollowB); - changed |= !newFollowB.equals(followB); - } else - { - // Identified A → αB => Apply rule 3: Every element of FOLLOW(A) is also an element of FOLLOW(B) - final EfficientTerminalSet followA = data.get(nonTerminalA); - final EfficientTerminalSet newFollowB = followB.plusAll(followA); - - - // Finish: put new FOLLOW(B) into data map and check for changes - data.put(nonTerminalB, newFollowB); - changed |= !newFollowB.equals(followB); - } - } - } while (changed); - } - - - /** - * @param nonTerminal - * @return Gets the FOLLOW set for the given non-terminal. - */ - public EfficientTerminalSet get(NonTerminal nonTerminal) - { - return data.get(nonTerminal); - } +public class FollowSets { + private final Map data; + + /** + * @param grammar + * @param firstSets + */ + public FollowSets(Grammar grammar, FirstSets firstSets) { + data = new HashMap(); + + // Initialize data map + final EfficientTerminalSet emptySet = new EfficientTerminalSet(grammar.getTerminals()); + for (NonTerminal nonTerminal : grammar.getNonTerminals()) { + data.put( + nonTerminal, + emptySet); // Viable because all modifiying operations will create new instances + } + + // Rule 1: For start symbol S, add $ to FOLLOW(S) + final NonTerminal startSymbol = grammar.getStartProduction().getLHS(); + final EfficientTerminalSet startSet = emptySet.plus(SpecialTerminals.EndOfInputStream); + data.put(startSymbol, startSet); + + // Actual algorithm: Apply rule 2 and 3 until nothing changes anymore + boolean changed; + do { + changed = false; + + for (Production production : grammar.getProductions()) { + // Rule 2: For a production A → αBβ, every element of FIRST(β) (w/o ɛ) is an element to + // FOLLOW(B) + // Rule 3: For productions A → αB (3.1) or A → αBβ with FIRST(β) containing ɛ (3.2), every + // element of + // FOLLOW(A) is also an element of FOLLOW(B) + + // ### Find B (A → αBβ / A → αB => first non-terminal in rhs) ... + final List rhs = production.getRHS(); + + NonTerminal nonTerminalB = null; + Symbol nextSymbol = null; + + final Iterator rhsIt = rhs.iterator(); + for (; rhsIt.hasNext(); ) { + Symbol symbol = rhsIt.next(); + if (symbol instanceof NonTerminal) { + nonTerminalB = (NonTerminal) symbol; + + // Great, found non-terminal B! Check for β... + if (rhsIt.hasNext()) { + nextSymbol = rhsIt.next(); + } + + break; + } + } + + if (nonTerminalB == null) { + // If there's no non-terminal: continue with next production + continue; + } + + // ### Check for rule 2 and 3 + final boolean notLastSymbol = !(nextSymbol == null); + final NonTerminal nonTerminalA = production.getLHS(); + final EfficientTerminalSet followB = data.get(nonTerminalB); + + // Differ between αBβ and αB ... + if (notLastSymbol) { + // Identified A → αBβ => Apply rule 2: Every element of FIRST(β) (w/o ɛ) is added to + // FOLLOW(B) + // Important: β may be a list of Symbols! If a first set for a symbol contains ɛ, the + // following must be considered, to! + final Symbol beta = nextSymbol; + EfficientTerminalSet firstBeta = firstSets.get(beta); + + // Aggregate first sets if all sets before contained ɛ + while (rhsIt.hasNext() && firstBeta.contains(SpecialTerminals.Epsilon)) { + Symbol symbol = rhsIt.next(); + final EfficientTerminalSet firstNextBeta = firstSets.get(symbol); + firstBeta = firstBeta.plusAll(firstNextBeta); + } + EfficientTerminalSet newFollowB = followB.plusAllExceptEpsilon(firstBeta); + + // Check for condition 3.2: FIRST(β) contains ɛ + if (firstBeta.contains(SpecialTerminals.Epsilon)) { + // Apply rule 3: Every element of FOLLOW(A) is also an element of FOLLOW(B) + final EfficientTerminalSet followA = data.get(nonTerminalA); + newFollowB = newFollowB.plusAll(followA); + } + + // Finish: put new FOLLOW(B) into data map and check for changes + data.put(nonTerminalB, newFollowB); + changed |= !Objects.equals(newFollowB, followB); + } else { + // Identified A → αB => Apply rule 3: Every element of FOLLOW(A) is also an element of + // FOLLOW(B) + final EfficientTerminalSet followA = data.get(nonTerminalA); + final EfficientTerminalSet newFollowB = followB.plusAll(followA); + + // Finish: put new FOLLOW(B) into data map and check for changes + data.put(nonTerminalB, newFollowB); + changed |= !Objects.equals(newFollowB, followB); + } + } + } while (changed); + } + + /** + * @param nonTerminal + * @return Gets the FOLLOW set for the given non-terminal. + */ + public EfficientTerminalSet get(NonTerminal nonTerminal) { + return data.get(nonTerminal); + } } diff --git a/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/generator/LALR1AutomatonFactory.java b/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/generator/LALR1AutomatonFactory.java index cf5f14915..2f83b8a6f 100644 --- a/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/generator/LALR1AutomatonFactory.java +++ b/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/generator/LALR1AutomatonFactory.java @@ -2,205 +2,184 @@ import static edu.tum.cup2.grammar.SpecialTerminals.EndOfInputStream; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.Set; - import edu.tum.cup2.generator.exceptions.GeneratorException; import edu.tum.cup2.generator.items.*; import edu.tum.cup2.generator.states.*; import edu.tum.cup2.grammar.Symbol; - +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.Objects; +import java.util.Set; /** * Factory for a LALR(1) automaton. - * - * TIDY: remove hashsets/maps from {@link AutomatonFactory} since each - * factory works differently... at least I think so (Andi). - * + * + *

TIDY: remove hashsets/maps from {@link AutomatonFactory} since each factory works + * differently... at least I think so (Andi). + * * @author Andreas Wenger * @author Daniel Altmann * @author Michael Hausmann */ -public class LALR1AutomatonFactory extends AutomatonFactory -{ - - private class NumberedEdge - { - public final Integer srcState; - public final Integer destState; //null means: accept - public final Symbol symbol; - public final LR1Item srcItem; - - public NumberedEdge(Integer srcState, Integer destState, Symbol symbol, LR1Item srcItem) - { - this.srcState = srcState; - this.destState = destState; - this.symbol = symbol; - this.srcItem = srcItem; - } - - @Override public boolean equals(Object obj) - { - if (obj instanceof NumberedEdge) - { - NumberedEdge e = (NumberedEdge) obj; - return srcState.equals(e.srcState) && symbol.equals(e.symbol); - } - return false; - } - - - @Override public int hashCode() - { - return srcState * 100 + symbol.hashCode(); - } - } - - - /** - * Create an LALR(1)-Automaton. - */ - public Automaton createAutomaton(LRGenerator generator, - GrammarInfo grammarInfo) - throws GeneratorException - { - - this.generator = generator; - this.grammarInfo = grammarInfo; - initCreation(); - - //hashmap which matches a kernel of a state to its complete state - HashMap newDFAStates = new HashMap(); - - //hashmap which matches an integer number to a state (and backwards). this allows merging a state - //with a new one without changing its number, so that we don't need to update the - //edges each time we merge statesState - HashMap numberedStates = new HashMap(); - HashMap statesNumbered = new HashMap(); - - //but this means also that we need to store the source and destination states - //of the edges in this format - HashSet numberedEdges = new HashSet(); - - //our queue consists of numbers instead of states - LinkedList newQueue = new LinkedList(); - LR1State state = queue.remove(0); - numberedStates.put(0, state); - statesNumbered.put(state, 0); - newQueue.add(0); - - //to find out quickly if a state is already queued, we use an additional hashset - HashSet queuedStateNumbers = new HashSet(); - queuedStateNumbers.add(0); - - //for all states, find their edges to other (possibly new) states - while (!newQueue.isEmpty()) - //Appel says: "until E and T did not change in this iteration". - //but: do we really need E here? I ignored it - { - //handle next state in queue - Integer stateNumber = newQueue.removeFirst(); - queuedStateNumbers.remove(stateNumber); - stateKernel = numberedStates.get(stateNumber); //get kernel of the state - - //debug messages - printDebugMessages(); - state = stateKernel.closure(grammarInfo); //unpack state (from kernel to closure) - Set shiftedSymbols = new HashSet(); - for (LR1Item item : state.getItems()) - { - - if (item.isShiftable()) - { - Symbol symbol = item.getNextSymbol(); - if (symbol == EndOfInputStream) - { - //$-symbol: here we accept - numberedEdges.add(new NumberedEdge(stateNumber, null, symbol, item)); - } - else if (!shiftedSymbols.contains(symbol)) - { - shiftedSymbols.add(symbol); - - //terminal or non-terminal - - //shift to other state - LR1State shiftedState = (LR1State) state.goTo(symbol); - LR0State shiftedStateKernel = shiftedState.getLR0Kernel(); - //its number is still unknown. it depends on the existence of a LR(0)-equal state - Integer shiftedStateNumber; - - //we try to find out if there is already some state which has an equal - //kernel to the shifted state - LR1State equalStateLR0 = newDFAStates.get(shiftedStateKernel); - boolean foundEqualState = (equalStateLR0 != null); - - //add the shifted state to the queue (again)? - boolean addShiftedStateToQueue = false; - - if (foundEqualState) - { - //in case they are equal we merge the states - LR1State mergedState = equalStateLR0.merge(shiftedState); - shiftedStateNumber = statesNumbered.get(equalStateLR0); - - //if merged state is different to the old state, new information - //was found and we have to add it to the queue again - if (!mergedState.equals(equalStateLR0)) - { - //replace state in hashmap. its number stays the same. - statesNumbered.remove(equalStateLR0); - numberedStates.put(shiftedStateNumber, mergedState); - statesNumbered.put(mergedState, shiftedStateNumber); - newDFAStates.put(mergedState.getLR0Kernel(), mergedState); - addShiftedStateToQueue = true; - } - } - else - { - //create new state with new number - LR1State newState = shiftedState; - shiftedStateNumber = numberedStates.size(); - newDFAStates.put(newState.getLR0Kernel(), newState); - numberedStates.put(shiftedStateNumber, newState); - statesNumbered.put(newState, shiftedStateNumber); - addShiftedStateToQueue = true; - } - - //add edge - numberedEdges.add(new NumberedEdge(stateNumber, shiftedStateNumber, symbol, item)); - - //queue shifted state if requested, but only if not already in queue - if (addShiftedStateToQueue && !queuedStateNumbers.contains(shiftedStateNumber)) - { - newQueue.add(shiftedStateNumber); - queuedStateNumbers.add(shiftedStateNumber); - } - - } - } - } - } - - //fill dfaStates - for (LR1State s : numberedStates.values()) - { - dfaStates.add(s); - } - - //fill dfaEdges - for (NumberedEdge e : numberedEdges) - { - dfaEdges.add(new Edge(numberedStates.get(e.srcState), e.symbol, - numberedStates.get(e.destState), e.srcItem.getLR0Kernel())); - } - - printDebugResult(); - - return ret; - } - +public class LALR1AutomatonFactory extends AutomatonFactory { + + private class NumberedEdge { + public final Integer srcState; + public final Integer destState; // null means: accept + public final Symbol symbol; + public final LR1Item srcItem; + + public NumberedEdge(Integer srcState, Integer destState, Symbol symbol, LR1Item srcItem) { + this.srcState = srcState; + this.destState = destState; + this.symbol = symbol; + this.srcItem = srcItem; + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof NumberedEdge) { + NumberedEdge e = (NumberedEdge) obj; + return Objects.equals(srcState, e.srcState) && Objects.equals(symbol, e.symbol); + } + return false; + } + + @Override + public int hashCode() { + return srcState * 100 + symbol.hashCode(); + } + } + + /** Create an LALR(1)-Automaton. */ + public Automaton createAutomaton( + LRGenerator generator, GrammarInfo grammarInfo) throws GeneratorException { + + this.generator = generator; + this.grammarInfo = grammarInfo; + initCreation(); + + // hashmap which matches a kernel of a state to its complete state + HashMap newDFAStates = new HashMap(); + + // hashmap which matches an integer number to a state (and backwards). this allows merging a + // state + // with a new one without changing its number, so that we don't need to update the + // edges each time we merge statesState + HashMap numberedStates = new HashMap(); + HashMap statesNumbered = new HashMap(); + + // but this means also that we need to store the source and destination states + // of the edges in this format + HashSet numberedEdges = new HashSet(); + + // our queue consists of numbers instead of states + LinkedList newQueue = new LinkedList(); + LR1State state = queue.remove(0); + numberedStates.put(0, state); + statesNumbered.put(state, 0); + newQueue.add(0); + + // to find out quickly if a state is already queued, we use an additional hashset + HashSet queuedStateNumbers = new HashSet(); + queuedStateNumbers.add(0); + + // for all states, find their edges to other (possibly new) states + while (!newQueue.isEmpty()) + // Appel says: "until E and T did not change in this iteration". + // but: do we really need E here? I ignored it + { + // handle next state in queue + Integer stateNumber = newQueue.removeFirst(); + queuedStateNumbers.remove(stateNumber); + stateKernel = numberedStates.get(stateNumber); // get kernel of the state + + // debug messages + printDebugMessages(); + state = stateKernel.closure(grammarInfo); // unpack state (from kernel to closure) + Set shiftedSymbols = new HashSet(); + for (LR1Item item : state.getItems()) { + + if (item.isShiftable()) { + Symbol symbol = item.getNextSymbol(); + if (symbol == EndOfInputStream) { + // $-symbol: here we accept + numberedEdges.add(new NumberedEdge(stateNumber, null, symbol, item)); + } else if (!shiftedSymbols.contains(symbol)) { + shiftedSymbols.add(symbol); + + // terminal or non-terminal + + // shift to other state + LR1State shiftedState = (LR1State) state.goTo(symbol); + LR0State shiftedStateKernel = shiftedState.getLR0Kernel(); + // its number is still unknown. it depends on the existence of a LR(0)-equal state + Integer shiftedStateNumber; + + // we try to find out if there is already some state which has an equal + // kernel to the shifted state + LR1State equalStateLR0 = newDFAStates.get(shiftedStateKernel); + boolean foundEqualState = (equalStateLR0 != null); + + // add the shifted state to the queue (again)? + boolean addShiftedStateToQueue = false; + + if (foundEqualState) { + // in case they are equal we merge the states + LR1State mergedState = equalStateLR0.merge(shiftedState); + shiftedStateNumber = statesNumbered.get(equalStateLR0); + + // if merged state is different to the old state, new information + // was found and we have to add it to the queue again + if (!Objects.equals(mergedState, equalStateLR0)) { + // replace state in hashmap. its number stays the same. + statesNumbered.remove(equalStateLR0); + numberedStates.put(shiftedStateNumber, mergedState); + statesNumbered.put(mergedState, shiftedStateNumber); + newDFAStates.put(mergedState.getLR0Kernel(), mergedState); + addShiftedStateToQueue = true; + } + } else { + // create new state with new number + LR1State newState = shiftedState; + shiftedStateNumber = numberedStates.size(); + newDFAStates.put(newState.getLR0Kernel(), newState); + numberedStates.put(shiftedStateNumber, newState); + statesNumbered.put(newState, shiftedStateNumber); + addShiftedStateToQueue = true; + } + + // add edge + numberedEdges.add(new NumberedEdge(stateNumber, shiftedStateNumber, symbol, item)); + + // queue shifted state if requested, but only if not already in queue + if (addShiftedStateToQueue && !queuedStateNumbers.contains(shiftedStateNumber)) { + newQueue.add(shiftedStateNumber); + queuedStateNumbers.add(shiftedStateNumber); + } + } + } + } + } + + // fill dfaStates + for (LR1State s : numberedStates.values()) { + dfaStates.add(s); + } + + // fill dfaEdges + for (NumberedEdge e : numberedEdges) { + dfaEdges.add( + new Edge( + numberedStates.get(e.srcState), + e.symbol, + numberedStates.get(e.destState), + e.srcItem.getLR0Kernel())); + } + + printDebugResult(); + return ret; + } } diff --git a/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/generator/LALR1CPAutomatonFactory.java b/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/generator/LALR1CPAutomatonFactory.java index 07e87965b..1f087f997 100644 --- a/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/generator/LALR1CPAutomatonFactory.java +++ b/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/generator/LALR1CPAutomatonFactory.java @@ -6,11 +6,6 @@ import static edu.tum.cup2.util.CollectionTools.map; import static edu.tum.cup2.util.CollectionTools.set; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - import edu.tum.cup2.generator.exceptions.GeneratorException; import edu.tum.cup2.generator.items.CPGoToLink; import edu.tum.cup2.generator.items.LALR1CPItem; @@ -21,232 +16,215 @@ import edu.tum.cup2.generator.terminals.EfficientTerminalSet; import edu.tum.cup2.grammar.Symbol; import edu.tum.cup2.util.Tuple2; - +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; /** - * Factory for a LALR(1) automaton, that works without side effects - * (unlike {@link LALR1CPAutomatonFactory}). - * + * Factory for a LALR(1) automaton, that works without side effects (unlike {@link + * LALR1CPAutomatonFactory}). + * * @author Andreas Wenger * @author Daniel Altmann * @author Michael Hausmann */ -public class LALR1CPAutomatonFactory extends AutomatonFactory -{ - - - /** - * Create an LALR(1)-Automaton. - */ - public Automaton createAutomaton(LRGenerator generator, - GrammarInfo grammarInfo) - throws GeneratorException - { - - this.generator = generator; - this.grammarInfo = grammarInfo; - initCreation(); - -// long time0 = System.currentTimeMillis(); - - //create the start state (start production with dot at position 0 and "Placeholder" as lookahead) - LR0Item startStateKernelItem = this.queue.remove(0).getLR0Kernel().getFirstItem(); - Set startStateItemKernel = set(); - startStateItemKernel.add(startStateKernelItem); - Set startStateItem = set(); - startStateItem.add(new LALR1CPItem(startStateKernelItem, grammarInfo.getTerminalSet(Placeholder))); - LALR1CPState startStateKernel = new LALR1CPState( - startStateItem); - - //hashmap which matches a kernel of a state - //to its complete (with closure) state - Map kernel2closure = map(); - - //collected go-to context propagation links - //(key: source item, value contains target state *closure*) - Map goToLinks = map(); - - //which items belong to which state (closure) - Map itemStates = map(); - - //set of edges - Set lr0Edges = set(); - - //TODO: own function - { - - //initialize queue, which consists of states - Set queue = set(); - queue.add(startStateKernel); - kernel2closure.put(startStateKernel, startStateKernel.closure(grammarInfo)); - - //for all states, find their edges to other (possibly new) states - while (!queue.isEmpty()) - //Appel says: "until E and T did not change in this iteration". - //but: do we really need E here? I ignored it - { - //handle next state in queue - LALR1CPState stateKernel = queue.iterator().next(); - queue.remove(stateKernel); - - //debug messages - printDebugMessages(); - - //first, create a LALR(1)-with-CP-Links automaton and remember the closures - //for performance reasons - LALR1CPState state = kernel2closure.get(stateKernel); - Set shiftedSymbols = new HashSet(); - for (LALR1CPItem item : state.getItems()) - { - //remember the state this item belongs to - itemStates.put(item, state); - //try to shift - if (item.isShiftable()) - { - Symbol symbol = item.getNextSymbol(); - if (symbol == EndOfInputStream) - { - //$-symbol: here we accept - lr0Edges.add(createAcceptEdge(stateKernel, symbol)); //GOON: with or without closure? - } - else if (shiftedSymbols.add(symbol)) //shift each symbol only once - { - //terminal or non-terminal - - //shift to other state - Tuple2> s = state.goToCP(symbol); - LALR1CPState shiftedStateKernel = s.get1(); - List shiftedStateCPLinks = s.get2(); - - //we try to find out if there is already some state which has an equal - //kernel to the shifted state (LALR1CPState equals on kernel) - LALR1CPState equalStateLALR1CP = kernel2closure.get(shiftedStateKernel); - LALR1CPState gotoLinkTargetState = equalStateLALR1CP; - if (equalStateLALR1CP == null) - { - //add new state - LALR1CPState shiftedState = shiftedStateKernel.closure(grammarInfo); - kernel2closure.put(shiftedStateKernel, shiftedState); - queue.add(shiftedStateKernel); - gotoLinkTargetState = shiftedState; - - } - - //remember CP links (cp link contains closure of target state, not only kernel) - for (CPGoToLink link : shiftedStateCPLinks) - { - LALR1CPItem todoItem = link.getSource(); - if (goToLinks.containsKey(todoItem)) - throw new RuntimeException("Double gotoLink!"); - goToLinks.put(todoItem, link.withTargetState(gotoLinkTargetState)); - } - - //add edge - lr0Edges.add(new Edge(stateKernel, symbol, shiftedStateKernel, item.getLR0Item())); - } - } - } - } - } - - Map lookaheads = map(); - //TODO: own function - { - //now, since we have built the LALR(1)-CP automaton, we compute - //all lookaheads by just following the CP links. Therefore, we just save the lookaheads - //for each LALR1CPItem in a hashmap - - - //initialize queue (consisting of kernels) with the start item kernel - Set queue = set(); - LALR1CPState st = kernel2closure.get(startStateKernel); - LALR1CPItem firstItem = st.getItemWithLookaheadByLR0Item(startStateKernelItem); - queue.add(firstItem); - lookaheads.put(firstItem, firstItem.getLookaheads()); - - EfficientTerminalSet empty = firstItem.getLookaheads().empty(); - for (LALR1CPState sta: kernel2closure.values()){ - for (LALR1CPItem ite : sta.getItems()){ - if (ite.getPosition()==0) { - queue.add(ite); - lookaheads.put(ite,ite.getLookaheads()); - } - else - lookaheads.put(ite,empty); - } - } - - while (!queue.isEmpty()) - { - LALR1CPItem item = queue.iterator().next(); - queue.remove(item); - EfficientTerminalSet sourceItemLookaheads = lookaheads.get(item); - - //go-to-links: propagate lookaheads to all target items - CPGoToLink gotoLink = goToLinks.get(item); - if (gotoLink != null) - { - LALR1CPState targetState = gotoLink.getTargetState(); - LALR1CPItem targetItem = targetState.getItemWithLookaheadByLR0Item(gotoLink.getTargetItem()); - //add lookaheads to target item - //if new lookaheads were found, add target item to the queue - EfficientTerminalSet before = lookaheads.get(targetItem); - EfficientTerminalSet after = before.plusAll(sourceItemLookaheads); - if (!before.equals(after)) - { - lookaheads.put(targetItem, after); - queue.add(targetItem); - } - } - - //closure-links - for (LR0Item closureLink : item.getClosureLinks()) - { - LALR1CPState targetState = itemStates.get(item); //same state as current item - LALR1CPItem targetItem = targetState.getItemWithLookaheadByLR0Item(closureLink); - //add lookaheads to target item - //if new lookaheads were found, add target item to the queue - EfficientTerminalSet before = lookaheads.get(targetItem); - EfficientTerminalSet after = before.plusAll(sourceItemLookaheads); - after=after.plusAll(targetItem.getLookaheads()); - if (!before.equals(after)) - { - lookaheads.put(targetItem, after); - queue.add(targetItem); - } - } - - } - } - - //create states and edges from collected information - Map lalr1CPToLR1Map = map(); - for (LALR1CPState state : kernel2closure.keySet()) - { - HashSet lr1Items = new HashSet(); - LALR1CPState stateWithClosure = kernel2closure.get(state); - - for (LR0Item strippedItem : state.getStrippedItems()){ - LALR1CPItem item = stateWithClosure.getItemWithLookaheadByLR0Item(strippedItem); - EfficientTerminalSet terminals = lookaheads.get(item); - lr1Items.add(new LR1Item(strippedItem, terminals)); - } - LR1State lr1State = new LR1State(lr1Items); - lalr1CPToLR1Map.put(state, lr1State); - dfaStates.add(lr1State); - } - - //fill dfaEdges - for (Edge edge : lr0Edges) - { - this.dfaEdges.add(new Edge(lalr1CPToLR1Map.get(edge.getSrc()), - edge.getSymbol(), lalr1CPToLR1Map.get(edge.getDest()), edge.getSrcItem())); - } - - printDebugResult(); - - return ret; - } - +public class LALR1CPAutomatonFactory extends AutomatonFactory { + + /** Create an LALR(1)-Automaton. */ + public Automaton createAutomaton( + LRGenerator generator, GrammarInfo grammarInfo) throws GeneratorException { + + this.generator = generator; + this.grammarInfo = grammarInfo; + initCreation(); + + // long time0 = System.currentTimeMillis(); + + // create the start state (start production with dot at position 0 and "Placeholder" as + // lookahead) + LR0Item startStateKernelItem = this.queue.remove(0).getLR0Kernel().getFirstItem(); + Set startStateItemKernel = set(); + startStateItemKernel.add(startStateKernelItem); + Set startStateItem = set(); + startStateItem.add( + new LALR1CPItem(startStateKernelItem, grammarInfo.getTerminalSet(Placeholder))); + LALR1CPState startStateKernel = new LALR1CPState(startStateItem); + + // hashmap which matches a kernel of a state + // to its complete (with closure) state + Map kernel2closure = map(); + + // collected go-to context propagation links + // (key: source item, value contains target state *closure*) + Map goToLinks = map(); + + // which items belong to which state (closure) + Map itemStates = map(); + + // set of edges + Set lr0Edges = set(); + + // TODO: own function + { + + // initialize queue, which consists of states + Set queue = set(); + queue.add(startStateKernel); + kernel2closure.put(startStateKernel, startStateKernel.closure(grammarInfo)); + + // for all states, find their edges to other (possibly new) states + while (!queue.isEmpty()) + // Appel says: "until E and T did not change in this iteration". + // but: do we really need E here? I ignored it + { + // handle next state in queue + LALR1CPState stateKernel = queue.iterator().next(); + queue.remove(stateKernel); + + // debug messages + printDebugMessages(); + + // first, create a LALR(1)-with-CP-Links automaton and remember the closures + // for performance reasons + LALR1CPState state = kernel2closure.get(stateKernel); + Set shiftedSymbols = new HashSet(); + for (LALR1CPItem item : state.getItems()) { + // remember the state this item belongs to + itemStates.put(item, state); + // try to shift + if (item.isShiftable()) { + Symbol symbol = item.getNextSymbol(); + if (symbol == EndOfInputStream) { + // $-symbol: here we accept + lr0Edges.add(createAcceptEdge(stateKernel, symbol)); // GOON: with or without closure? + } else if (shiftedSymbols.add(symbol)) // shift each symbol only once + { + // terminal or non-terminal + + // shift to other state + Tuple2> s = state.goToCP(symbol); + LALR1CPState shiftedStateKernel = s.get1(); + List shiftedStateCPLinks = s.get2(); + + // we try to find out if there is already some state which has an equal + // kernel to the shifted state (LALR1CPState equals on kernel) + LALR1CPState equalStateLALR1CP = kernel2closure.get(shiftedStateKernel); + LALR1CPState gotoLinkTargetState = equalStateLALR1CP; + if (equalStateLALR1CP == null) { + // add new state + LALR1CPState shiftedState = shiftedStateKernel.closure(grammarInfo); + kernel2closure.put(shiftedStateKernel, shiftedState); + queue.add(shiftedStateKernel); + gotoLinkTargetState = shiftedState; + } + + // remember CP links (cp link contains closure of target state, not only kernel) + for (CPGoToLink link : shiftedStateCPLinks) { + LALR1CPItem todoItem = link.getSource(); + if (goToLinks.containsKey(todoItem)) throw new RuntimeException("Double gotoLink!"); + goToLinks.put(todoItem, link.withTargetState(gotoLinkTargetState)); + } + + // add edge + lr0Edges.add(new Edge(stateKernel, symbol, shiftedStateKernel, item.getLR0Item())); + } + } + } + } + } + + Map lookaheads = map(); + // TODO: own function + { + // now, since we have built the LALR(1)-CP automaton, we compute + // all lookaheads by just following the CP links. Therefore, we just save the lookaheads + // for each LALR1CPItem in a hashmap + + // initialize queue (consisting of kernels) with the start item kernel + Set queue = set(); + LALR1CPState st = kernel2closure.get(startStateKernel); + LALR1CPItem firstItem = st.getItemWithLookaheadByLR0Item(startStateKernelItem); + queue.add(firstItem); + lookaheads.put(firstItem, firstItem.getLookaheads()); + + EfficientTerminalSet empty = firstItem.getLookaheads().empty(); + for (LALR1CPState sta : kernel2closure.values()) { + for (LALR1CPItem ite : sta.getItems()) { + if (ite.getPosition() == 0) { + queue.add(ite); + lookaheads.put(ite, ite.getLookaheads()); + } else lookaheads.put(ite, empty); + } + } + + while (!queue.isEmpty()) { + LALR1CPItem item = queue.iterator().next(); + queue.remove(item); + EfficientTerminalSet sourceItemLookaheads = lookaheads.get(item); + + // go-to-links: propagate lookaheads to all target items + CPGoToLink gotoLink = goToLinks.get(item); + if (gotoLink != null) { + LALR1CPState targetState = gotoLink.getTargetState(); + LALR1CPItem targetItem = + targetState.getItemWithLookaheadByLR0Item(gotoLink.getTargetItem()); + // add lookaheads to target item + // if new lookaheads were found, add target item to the queue + EfficientTerminalSet before = lookaheads.get(targetItem); + EfficientTerminalSet after = before.plusAll(sourceItemLookaheads); + if (!Objects.equals(before, after)) { + lookaheads.put(targetItem, after); + queue.add(targetItem); + } + } + + // closure-links + for (LR0Item closureLink : item.getClosureLinks()) { + LALR1CPState targetState = itemStates.get(item); // same state as current item + LALR1CPItem targetItem = targetState.getItemWithLookaheadByLR0Item(closureLink); + // add lookaheads to target item + // if new lookaheads were found, add target item to the queue + EfficientTerminalSet before = lookaheads.get(targetItem); + EfficientTerminalSet after = before.plusAll(sourceItemLookaheads); + after = after.plusAll(targetItem.getLookaheads()); + if (!Objects.equals(before, after)) { + lookaheads.put(targetItem, after); + queue.add(targetItem); + } + } + } + } + + // create states and edges from collected information + Map lalr1CPToLR1Map = map(); + for (LALR1CPState state : kernel2closure.keySet()) { + HashSet lr1Items = new HashSet(); + LALR1CPState stateWithClosure = kernel2closure.get(state); + + for (LR0Item strippedItem : state.getStrippedItems()) { + LALR1CPItem item = stateWithClosure.getItemWithLookaheadByLR0Item(strippedItem); + EfficientTerminalSet terminals = lookaheads.get(item); + lr1Items.add(new LR1Item(strippedItem, terminals)); + } + LR1State lr1State = new LR1State(lr1Items); + lalr1CPToLR1Map.put(state, lr1State); + dfaStates.add(lr1State); + } + + // fill dfaEdges + for (Edge edge : lr0Edges) { + this.dfaEdges.add( + new Edge( + lalr1CPToLR1Map.get(edge.getSrc()), + edge.getSymbol(), + lalr1CPToLR1Map.get(edge.getDest()), + edge.getSrcItem())); + } + + printDebugResult(); + return ret; + } } diff --git a/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/generator/LALR1ParallelAutomatonFactory.java b/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/generator/LALR1ParallelAutomatonFactory.java index 9f0d2634b..557f1b436 100755 --- a/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/generator/LALR1ParallelAutomatonFactory.java +++ b/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/generator/LALR1ParallelAutomatonFactory.java @@ -5,16 +5,6 @@ import static edu.tum.cup2.grammar.SpecialTerminals.Placeholder; import static edu.tum.cup2.util.CollectionTools.*; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.locks.ReentrantLock; - import edu.tum.cup2.generator.exceptions.GeneratorException; import edu.tum.cup2.generator.items.CPGoToLink; import edu.tum.cup2.generator.items.LALR1CPItem; @@ -25,376 +15,389 @@ import edu.tum.cup2.generator.terminals.EfficientTerminalSet; import edu.tum.cup2.grammar.Symbol; import edu.tum.cup2.util.Tuple2; - +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.ReentrantLock; /** - * Factory for a LALR(1) automaton, that is computed concurrently. - * (based on {@link LALR1CPAutomatonFactory}). - * + * Factory for a LALR(1) automaton, that is computed concurrently. (based on {@link + * LALR1CPAutomatonFactory}). + * * @author Andreas Wenger * @author Daniel Altmann * @author Michael Hausmann * @author Johannes Schamburger */ -public class LALR1ParallelAutomatonFactory extends AutomatonFactory -{ - - protected Map kernel2closure; - protected Map itemStates; - protected Set lr0Edges; - protected Map goToLinks; - private Map lookaheads; - - /** - * Task for the first step of the efficient LALR(1) parser generation. - * Creates the LR(0) automaton together with context propagation links. - */ - private class WorkerTaskLALR1 extends WorkerTask { - - public WorkerTaskLALR1(LALR1CPState state, ThreadPoolExecutor threadPool, ReentrantLock lock) { - super(state, threadPool, lock); - } - - /** - * Handles one state; creates new tasks by shifting. - * @return 0 - we don't care about the return value, but the {@link Callable} - * interface forces us to return an Integer. - */ - public Integer call() { - - try { - //handle next state in queue - - //debug messages - printDebugMessages(); - - //first, create a LALR(1)-with-CP-Links automaton and remember the closures - //for performance reasons - currentState = kernel2closure.get(stateKernel); - - Set shiftedSymbols = new HashSet(); - for (LALR1CPItem item : currentState.getItems()) - { - //remember the state this item belongs to - itemStates.put(item, currentState); - //try to shift - if (item.isShiftable()) - { - Symbol symbol = item.getNextSymbol(); - if (symbol == EndOfInputStream) - { - //$-symbol: here we accept - lr0Edges.add(createAcceptEdge(stateKernel, symbol)); //GOON: with or without closure? - } - else if (shiftedSymbols.add(symbol)) //shift each symbol only once - { - //terminal or non-terminal - - //shift to other state - Tuple2> s = currentState.goToCP(symbol); - LALR1CPState shiftedStateKernel = s.get1(); - List shiftedStateCPLinks = s.get2(); - - //we try to find out if there is already some state which has an equal - //kernel to the shifted state (LALR1CPState equals on kernel) - LALR1CPState equalStateLALR1CP; - LALR1CPState gotoLinkTargetState; - - //we need to synchronize here because we access kernel2closure twice; - //there must be no interruption between the get() and the put() call - synchronized(kernel2closure) { - equalStateLALR1CP = kernel2closure.get(shiftedStateKernel); - gotoLinkTargetState = equalStateLALR1CP; - if (equalStateLALR1CP == null) - { - //add new state - LALR1CPState shiftedState = shiftedStateKernel.closure(grammarInfo); - kernel2closure.put(shiftedStateKernel, shiftedState); - threadPool.submit(new WorkerTaskLALR1(shiftedStateKernel, threadPool, taskCountLock)); - gotoLinkTargetState = shiftedState; - } - } - - //remember CP links (cp link contains closure of target state, not only kernel) - for (CPGoToLink link : shiftedStateCPLinks) - { - LALR1CPItem todoItem = link.getSource(); - if(goToLinks.put(todoItem, link.withTargetState(gotoLinkTargetState)) != null) { - throw new RuntimeException("Double gotoLink!"); - } - } - - //add edge - lr0Edges.add(new Edge(stateKernel, symbol, shiftedStateKernel, item.getLR0Item())); - } - } - } - - decrementTaskCount(); - } catch (Throwable e) { - e.printStackTrace(); - } - return 0; - - } - - } - - /** - * Task for the second step of the efficient LALR(1) parser generation. - * Propagates the lookahead symbols along the context propagation links. - */ - private class WorkerTaskCPLinks extends WorkerTask { - - private LALR1CPItem currentItem; - - public String toString() { - return currentItem.toString(); - } - - public WorkerTaskCPLinks(LALR1CPItem item, ThreadPoolExecutor threadPool, ReentrantLock lock) { - super(null, threadPool, lock); // we don't care for the current state in this WorkerTask - currentItem = item; - } - - /** - * Handles one state; creates new tasks by following context propagation links. - * @return 0 - we don't care about the return value, but the {@link Callable} - * interface forces us to return an Integer. - */ - public Integer call() { - - try { - EfficientTerminalSet sourceItemLookaheads = lookaheads.get(currentItem); - - //go-to-links: propagate lookaheads to all target items - CPGoToLink gotoLink = goToLinks.get(currentItem); - if (gotoLink != null) - { - LALR1CPState targetState = gotoLink.getTargetState(); - LALR1CPItem targetItem = targetState.getItemWithLookaheadByLR0Item(gotoLink.getTargetItem()); - //add lookaheads to target item - //if new lookaheads were found, create a new WorkerTask for the target item - synchronized(lookaheads) { - EfficientTerminalSet before = lookaheads.get(targetItem); - EfficientTerminalSet after = before.plusAll(sourceItemLookaheads); - if(!before.equals(after)) { - lookaheads.put(targetItem,after); - threadPool.submit(new WorkerTaskCPLinks(targetItem, threadPool, taskCountLock)); - } - } - } - - //closure-links - for (LR0Item closureLink : currentItem.getClosureLinks()) - { - LALR1CPState targetState = itemStates.get(currentItem); //same state as current item - LALR1CPItem targetItem = targetState.getItemWithLookaheadByLR0Item(closureLink); - //add lookaheads to target item - //if new lookaheads were found, create a new WorkerTask for the target item - synchronized(lookaheads) { - EfficientTerminalSet before = lookaheads.get(targetItem); - EfficientTerminalSet after = before.plusAll(sourceItemLookaheads); - after = after.plusAll(targetItem.getLookaheads()); - if(!before.equals(after)) { - lookaheads.put(targetItem, after); - threadPool.submit(new WorkerTaskCPLinks(targetItem, threadPool, taskCountLock)); - } - } - } - } catch (Throwable e) { - e.printStackTrace(); - } - - decrementTaskCount(); - - return 0; - - } - } - - /** - * Create a LALR(1)-Automaton concurrently. - * @param generator - * @param grammarInfo - * @param numThreads the number of threads that will be used for the creation. - * @return the generated automaton. - * @throws GeneratorException - */ - public Automaton createAutomaton(LRGenerator generator, - GrammarInfo grammarInfo, int numThreads) - throws GeneratorException - { - - this.generator = generator; - this.grammarInfo = grammarInfo; - this.numThreads = numThreads; - - initCreation(); - - //create the start state (start production with dot at position 0 and "Placeholder" as lookahead) - LR0Item startStateKernelItem = this.queue.remove(0).getLR0Kernel().getFirstItem(); - Set startStateItemKernel = set(); - startStateItemKernel.add(startStateKernelItem); - Set startStateItem = set(); - startStateItem.add(new LALR1CPItem(startStateKernelItem, grammarInfo.getTerminalSet(Placeholder))); - LALR1CPState startStateKernel = new LALR1CPState( - startStateItem); - - kernel2closure = synchronizedMap(); - goToLinks = synchronizedMap(); - itemStates = synchronizedMap(); - lr0Edges = synchronizedSet(); - - threadPool = new ThreadPoolExecutor(numThreads, numThreads, Long.MAX_VALUE, TimeUnit.NANOSECONDS, new LinkedBlockingQueue()); - taskCountLock = new ReentrantLock(); - - shutDownThread = new Thread() { - @Override public void run() { - try { - synchronized(threadPool) { - threadPool.wait(); - } - } catch (InterruptedException e) { - e.printStackTrace(); - } - threadPool.shutdown(); - } - }; - // we need to ensure that the shutDownThread is started before the taskCount can be decremented to 0 - shutDownThread.start(); - // we need to ensure that the shutDownThread is already waiting to be notified - while(!(shutDownThread.getState()).equals(Thread.State.WAITING)) { - } - - // add one task for state0 to the thread pool and start the thread pool - LinkedList tasks = new LinkedList(); - kernel2closure.put(startStateKernel, startStateKernel.closure(grammarInfo)); - - taskCountLock.lock(); - try { - taskCount = 0; - } finally { - taskCountLock.unlock(); - } - tasks.add(new WorkerTaskLALR1(startStateKernel, threadPool, taskCountLock)); - try { - threadPool.invokeAll(tasks); - } catch (InterruptedException e) { - e.printStackTrace(); - } - - // wait until the ThreadPool has been shut down - try { - shutDownThread.join(); - } catch (InterruptedException e) { - e.printStackTrace(); - } - - lookaheads = synchronizedMap(); - - //now, since we have built the LALR(1)-CP automaton, we compute - //all lookaheads by just following the CP links. Therefore, we just save the lookaheads - //for each LALR1CPItem in a hashmap - - final ThreadPoolExecutor threadPoolCP = new ThreadPoolExecutor(numThreads, numThreads, Long.MAX_VALUE, TimeUnit.NANOSECONDS, new LinkedBlockingQueue()); - ReentrantLock taskCountLockCP = new ReentrantLock(); - - // create a new thread that waits until all tasks are done - Thread shutDownThreadCP = new Thread() { - @Override public void run() { - try { - synchronized(threadPoolCP) { - threadPoolCP.wait(); - } - } catch (InterruptedException e) { - e.printStackTrace(); - } - threadPoolCP.shutdown(); - } - }; - shutDownThreadCP.start(); - - // we need to ensure that the shutDownThread is already waiting to be notified - while(!(shutDownThreadCP.getState()).equals(Thread.State.WAITING)) { - } - - taskCountLockCP.lock(); - try { - taskCount = 0; - } finally { - taskCountLockCP.unlock(); - } - - //initialize queue (consisting of kernels) with the start item kernel - LinkedList tasksCP = new LinkedList(); - - LALR1CPState st = kernel2closure.get(startStateKernel); - LALR1CPItem firstItem = st.getItemWithLookaheadByLR0Item(startStateKernelItem); - tasksCP.add(new WorkerTaskCPLinks(firstItem, threadPoolCP, taskCountLockCP)); - lookaheads.put(firstItem, firstItem.getLookaheads()); - - EfficientTerminalSet empty = firstItem.getLookaheads().empty(); - for (LALR1CPState sta: kernel2closure.values()){ - for (LALR1CPItem ite : sta.getItems()){ - if (ite.getPosition()==0) { - tasksCP.add(new WorkerTaskCPLinks(ite, threadPoolCP, taskCountLockCP)); - lookaheads.put(ite,ite.getLookaheads()); - } - else - lookaheads.put(ite,empty); - } - } - - - try { - threadPoolCP.invokeAll(tasksCP); - } catch (InterruptedException e) { - e.printStackTrace(); - } - - // wait until the ThreadPool has been shut down - try { - shutDownThreadCP.join(); - } catch (InterruptedException e) { - e.printStackTrace(); - } - - //create states and edges from collected information - Map lalr1CPToLR1Map = map(); - for (LALR1CPState state : kernel2closure.keySet()) - { - HashSet lr1Items = new HashSet(); - LALR1CPState stateWithClosure = kernel2closure.get(state); - - for (LR0Item strippedItem : state.getStrippedItems()){ - LALR1CPItem item = stateWithClosure.getItemWithLookaheadByLR0Item(strippedItem); - EfficientTerminalSet terminals = lookaheads.get(item); - lr1Items.add(new LR1Item(strippedItem, terminals)); - } - LR1State lr1State = new LR1State(lr1Items); - lalr1CPToLR1Map.put(state, lr1State); - dfaStates.add(lr1State); - } - - //fill dfaEdges - for (Edge edge : lr0Edges) - { - this.dfaEdges.add(new Edge(lalr1CPToLR1Map.get(edge.getSrc()), - edge.getSymbol(), lalr1CPToLR1Map.get(edge.getDest()), edge.getSrcItem())); - } - - printDebugResult(); - - return ret; - } - - @Override - public Automaton createAutomaton( - LRGenerator generator, GrammarInfo grammarInfo) - throws GeneratorException { - return createAutomaton(generator, grammarInfo, defaultNumThreads); - } - +public class LALR1ParallelAutomatonFactory extends AutomatonFactory { + + protected Map kernel2closure; + protected Map itemStates; + protected Set lr0Edges; + protected Map goToLinks; + private Map lookaheads; + + /** + * Task for the first step of the efficient LALR(1) parser generation. Creates the LR(0) automaton + * together with context propagation links. + */ + private class WorkerTaskLALR1 extends WorkerTask { + + public WorkerTaskLALR1(LALR1CPState state, ThreadPoolExecutor threadPool, ReentrantLock lock) { + super(state, threadPool, lock); + } + + /** + * Handles one state; creates new tasks by shifting. + * + * @return 0 - we don't care about the return value, but the {@link Callable} interface forces + * us to return an Integer. + */ + public Integer call() { + + try { + // handle next state in queue + + // debug messages + printDebugMessages(); + + // first, create a LALR(1)-with-CP-Links automaton and remember the closures + // for performance reasons + currentState = kernel2closure.get(stateKernel); + + Set shiftedSymbols = new HashSet(); + for (LALR1CPItem item : currentState.getItems()) { + // remember the state this item belongs to + itemStates.put(item, currentState); + // try to shift + if (item.isShiftable()) { + Symbol symbol = item.getNextSymbol(); + if (symbol == EndOfInputStream) { + // $-symbol: here we accept + lr0Edges.add(createAcceptEdge(stateKernel, symbol)); // GOON: with or without closure? + } else if (shiftedSymbols.add(symbol)) // shift each symbol only once + { + // terminal or non-terminal + + // shift to other state + Tuple2> s = currentState.goToCP(symbol); + LALR1CPState shiftedStateKernel = s.get1(); + List shiftedStateCPLinks = s.get2(); + + // we try to find out if there is already some state which has an equal + // kernel to the shifted state (LALR1CPState equals on kernel) + LALR1CPState equalStateLALR1CP; + LALR1CPState gotoLinkTargetState; + + // we need to synchronize here because we access kernel2closure twice; + // there must be no interruption between the get() and the put() call + synchronized (kernel2closure) { + equalStateLALR1CP = kernel2closure.get(shiftedStateKernel); + gotoLinkTargetState = equalStateLALR1CP; + if (equalStateLALR1CP == null) { + // add new state + LALR1CPState shiftedState = shiftedStateKernel.closure(grammarInfo); + kernel2closure.put(shiftedStateKernel, shiftedState); + threadPool.submit( + new WorkerTaskLALR1(shiftedStateKernel, threadPool, taskCountLock)); + gotoLinkTargetState = shiftedState; + } + } + + // remember CP links (cp link contains closure of target state, not only kernel) + for (CPGoToLink link : shiftedStateCPLinks) { + LALR1CPItem todoItem = link.getSource(); + if (goToLinks.put(todoItem, link.withTargetState(gotoLinkTargetState)) != null) { + throw new RuntimeException("Double gotoLink!"); + } + } + + // add edge + lr0Edges.add(new Edge(stateKernel, symbol, shiftedStateKernel, item.getLR0Item())); + } + } + } + + decrementTaskCount(); + } catch (Throwable e) { + e.printStackTrace(); + } + return 0; + } + } + + /** + * Task for the second step of the efficient LALR(1) parser generation. Propagates the lookahead + * symbols along the context propagation links. + */ + private class WorkerTaskCPLinks extends WorkerTask { + + private LALR1CPItem currentItem; + + public String toString() { + return currentItem.toString(); + } + + public WorkerTaskCPLinks(LALR1CPItem item, ThreadPoolExecutor threadPool, ReentrantLock lock) { + super(null, threadPool, lock); // we don't care for the current state in this WorkerTask + currentItem = item; + } + + /** + * Handles one state; creates new tasks by following context propagation links. + * + * @return 0 - we don't care about the return value, but the {@link Callable} interface forces + * us to return an Integer. + */ + public Integer call() { + + try { + EfficientTerminalSet sourceItemLookaheads = lookaheads.get(currentItem); + + // go-to-links: propagate lookaheads to all target items + CPGoToLink gotoLink = goToLinks.get(currentItem); + if (gotoLink != null) { + LALR1CPState targetState = gotoLink.getTargetState(); + LALR1CPItem targetItem = + targetState.getItemWithLookaheadByLR0Item(gotoLink.getTargetItem()); + // add lookaheads to target item + // if new lookaheads were found, create a new WorkerTask for the target item + synchronized (lookaheads) { + EfficientTerminalSet before = lookaheads.get(targetItem); + EfficientTerminalSet after = before.plusAll(sourceItemLookaheads); + if (!Objects.equals(before, after)) { + lookaheads.put(targetItem, after); + threadPool.submit(new WorkerTaskCPLinks(targetItem, threadPool, taskCountLock)); + } + } + } + + // closure-links + for (LR0Item closureLink : currentItem.getClosureLinks()) { + LALR1CPState targetState = itemStates.get(currentItem); // same state as current item + LALR1CPItem targetItem = targetState.getItemWithLookaheadByLR0Item(closureLink); + // add lookaheads to target item + // if new lookaheads were found, create a new WorkerTask for the target item + synchronized (lookaheads) { + EfficientTerminalSet before = lookaheads.get(targetItem); + EfficientTerminalSet after = before.plusAll(sourceItemLookaheads); + after = after.plusAll(targetItem.getLookaheads()); + if (!Objects.equals(before, after)) { + lookaheads.put(targetItem, after); + threadPool.submit(new WorkerTaskCPLinks(targetItem, threadPool, taskCountLock)); + } + } + } + } catch (Throwable e) { + e.printStackTrace(); + } + + decrementTaskCount(); + + return 0; + } + } + + /** + * Create a LALR(1)-Automaton concurrently. + * + * @param generator + * @param grammarInfo + * @param numThreads the number of threads that will be used for the creation. + * @return the generated automaton. + * @throws GeneratorException + */ + public Automaton createAutomaton( + LRGenerator generator, GrammarInfo grammarInfo, int numThreads) + throws GeneratorException { + + this.generator = generator; + this.grammarInfo = grammarInfo; + this.numThreads = numThreads; + + initCreation(); + + // create the start state (start production with dot at position 0 and "Placeholder" as + // lookahead) + LR0Item startStateKernelItem = this.queue.remove(0).getLR0Kernel().getFirstItem(); + Set startStateItemKernel = set(); + startStateItemKernel.add(startStateKernelItem); + Set startStateItem = set(); + startStateItem.add( + new LALR1CPItem(startStateKernelItem, grammarInfo.getTerminalSet(Placeholder))); + LALR1CPState startStateKernel = new LALR1CPState(startStateItem); + + kernel2closure = synchronizedMap(); + goToLinks = synchronizedMap(); + itemStates = synchronizedMap(); + lr0Edges = synchronizedSet(); + + threadPool = + new ThreadPoolExecutor( + numThreads, + numThreads, + Long.MAX_VALUE, + TimeUnit.NANOSECONDS, + new LinkedBlockingQueue()); + taskCountLock = new ReentrantLock(); + + shutDownThread = + new Thread() { + @Override + public void run() { + try { + synchronized (threadPool) { + threadPool.wait(); + } + } catch (InterruptedException e) { + e.printStackTrace(); + } + threadPool.shutdown(); + } + }; + // we need to ensure that the shutDownThread is started before the taskCount can be decremented + // to 0 + shutDownThread.start(); + // we need to ensure that the shutDownThread is already waiting to be notified + while (!Objects.equals(shutDownThread.getState(), Thread.State.WAITING)) {} + + // add one task for state0 to the thread pool and start the thread pool + LinkedList tasks = new LinkedList(); + kernel2closure.put(startStateKernel, startStateKernel.closure(grammarInfo)); + + taskCountLock.lock(); + try { + taskCount = 0; + } finally { + taskCountLock.unlock(); + } + tasks.add(new WorkerTaskLALR1(startStateKernel, threadPool, taskCountLock)); + try { + threadPool.invokeAll(tasks); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + // wait until the ThreadPool has been shut down + try { + shutDownThread.join(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + lookaheads = synchronizedMap(); + + // now, since we have built the LALR(1)-CP automaton, we compute + // all lookaheads by just following the CP links. Therefore, we just save the lookaheads + // for each LALR1CPItem in a hashmap + + final ThreadPoolExecutor threadPoolCP = + new ThreadPoolExecutor( + numThreads, + numThreads, + Long.MAX_VALUE, + TimeUnit.NANOSECONDS, + new LinkedBlockingQueue()); + ReentrantLock taskCountLockCP = new ReentrantLock(); + + // create a new thread that waits until all tasks are done + Thread shutDownThreadCP = + new Thread() { + @Override + public void run() { + try { + synchronized (threadPoolCP) { + threadPoolCP.wait(); + } + } catch (InterruptedException e) { + e.printStackTrace(); + } + threadPoolCP.shutdown(); + } + }; + shutDownThreadCP.start(); + + // we need to ensure that the shutDownThread is already waiting to be notified + while (!Objects.equals(shutDownThreadCP.getState(), Thread.State.WAITING)) {} + + taskCountLockCP.lock(); + try { + taskCount = 0; + } finally { + taskCountLockCP.unlock(); + } + + // initialize queue (consisting of kernels) with the start item kernel + LinkedList tasksCP = new LinkedList(); + + LALR1CPState st = kernel2closure.get(startStateKernel); + LALR1CPItem firstItem = st.getItemWithLookaheadByLR0Item(startStateKernelItem); + tasksCP.add(new WorkerTaskCPLinks(firstItem, threadPoolCP, taskCountLockCP)); + lookaheads.put(firstItem, firstItem.getLookaheads()); + + EfficientTerminalSet empty = firstItem.getLookaheads().empty(); + for (LALR1CPState sta : kernel2closure.values()) { + for (LALR1CPItem ite : sta.getItems()) { + if (ite.getPosition() == 0) { + tasksCP.add(new WorkerTaskCPLinks(ite, threadPoolCP, taskCountLockCP)); + lookaheads.put(ite, ite.getLookaheads()); + } else lookaheads.put(ite, empty); + } + } + + try { + threadPoolCP.invokeAll(tasksCP); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + // wait until the ThreadPool has been shut down + try { + shutDownThreadCP.join(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + // create states and edges from collected information + Map lalr1CPToLR1Map = map(); + for (LALR1CPState state : kernel2closure.keySet()) { + HashSet lr1Items = new HashSet(); + LALR1CPState stateWithClosure = kernel2closure.get(state); + + for (LR0Item strippedItem : state.getStrippedItems()) { + LALR1CPItem item = stateWithClosure.getItemWithLookaheadByLR0Item(strippedItem); + EfficientTerminalSet terminals = lookaheads.get(item); + lr1Items.add(new LR1Item(strippedItem, terminals)); + } + LR1State lr1State = new LR1State(lr1Items); + lalr1CPToLR1Map.put(state, lr1State); + dfaStates.add(lr1State); + } + + // fill dfaEdges + for (Edge edge : lr0Edges) { + this.dfaEdges.add( + new Edge( + lalr1CPToLR1Map.get(edge.getSrc()), + edge.getSymbol(), + lalr1CPToLR1Map.get(edge.getDest()), + edge.getSrcItem())); + } + + printDebugResult(); + + return ret; + } + @Override + public Automaton createAutomaton( + LRGenerator generator, GrammarInfo grammarInfo) throws GeneratorException { + return createAutomaton(generator, grammarInfo, defaultNumThreads); + } } diff --git a/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/generator/LALR1SCCAutomatonFactory.java b/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/generator/LALR1SCCAutomatonFactory.java index ab206205e..36c73b964 100644 --- a/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/generator/LALR1SCCAutomatonFactory.java +++ b/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/generator/LALR1SCCAutomatonFactory.java @@ -9,13 +9,6 @@ import static edu.tum.cup2.util.CollectionTools.stack; import static edu.tum.cup2.util.Tuple2.t; -import java.util.Collection; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.Stack; - import edu.tum.cup2.generator.exceptions.GeneratorException; import edu.tum.cup2.generator.items.CPGoToLink; import edu.tum.cup2.generator.items.LALR1CPItem; @@ -27,13 +20,19 @@ import edu.tum.cup2.grammar.Symbol; import edu.tum.cup2.util.FibonacciHeap; import edu.tum.cup2.util.Tuple2; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.Stack; /** - * Factory for a LALR(1) automaton, that improves on - * {@link LALR1CPAutomatonFactory} by computing the context propagation - * with a StronglyConnectedComponents approach instead of slow fixpoint + * Factory for a LALR(1) automaton, that improves on {@link LALR1CPAutomatonFactory} by computing + * the context propagation with a StronglyConnectedComponents approach instead of slow fixpoint * iteration. - * + * * @author Andreas Wenger * @author Daniel Altmann * @author Michael Hausmann @@ -41,356 +40,346 @@ */ public class LALR1SCCAutomatonFactory extends AutomatonFactory { - /** - * Create an LALR(1)-Automaton. - */ - public Automaton createAutomaton(LRGenerator generator, - GrammarInfo grammarInfo) - throws GeneratorException { - - this.generator = generator; - this.grammarInfo = grammarInfo; - initCreation(); - - long start = System.currentTimeMillis(); - //debug=true; - //create the start state (start production with dot at position 0 and "Placeholder" as lookahead) - LR0Item startStateKernelItem = this.queue.remove(0).getLR0Kernel().getFirstItem(); - Set startStateItemKernel = set(); - startStateItemKernel.add(startStateKernelItem); - Set startStateItem = set(); - startStateItem.add(new LALR1CPItem(startStateKernelItem, grammarInfo.getTerminalSet(Placeholder))); - LALR1CPState startStateKernel = new LALR1CPState( - startStateItem); - - //hashmap which matches a kernel of a state - //to its complete (with closure) state - Map kernel2closure = map(); - - //collected go-to context propagation links - //(key: source item, value contains target state *closure*) - Map goToLinks = map(); - - //which items belong to which state (closure) - Map itemStates = map(); - - //set of edges - Set lr0Edges = set(); - - //TODO: own function - { - - //initialize queue, which consists of states - Set queue = set(); - queue.add(startStateKernel); - kernel2closure.put(startStateKernel, startStateKernel.closure(grammarInfo)); - - //for all states, find their edges to other (possibly new) states - while (!queue.isEmpty()) //Appel says: "until E and T did not change in this iteration". - //but: do we really need E here? I ignored it + /** Create an LALR(1)-Automaton. */ + public Automaton createAutomaton( + LRGenerator generator, GrammarInfo grammarInfo) throws GeneratorException { + + this.generator = generator; + this.grammarInfo = grammarInfo; + initCreation(); + + long start = System.currentTimeMillis(); + // debug=true; + // create the start state (start production with dot at position 0 and "Placeholder" as + // lookahead) + LR0Item startStateKernelItem = this.queue.remove(0).getLR0Kernel().getFirstItem(); + Set startStateItemKernel = set(); + startStateItemKernel.add(startStateKernelItem); + Set startStateItem = set(); + startStateItem.add( + new LALR1CPItem(startStateKernelItem, grammarInfo.getTerminalSet(Placeholder))); + LALR1CPState startStateKernel = new LALR1CPState(startStateItem); + + // hashmap which matches a kernel of a state + // to its complete (with closure) state + Map kernel2closure = map(); + + // collected go-to context propagation links + // (key: source item, value contains target state *closure*) + Map goToLinks = map(); + + // which items belong to which state (closure) + Map itemStates = map(); + + // set of edges + Set lr0Edges = set(); + + // TODO: own function + { + + // initialize queue, which consists of states + Set queue = set(); + queue.add(startStateKernel); + kernel2closure.put(startStateKernel, startStateKernel.closure(grammarInfo)); + + // for all states, find their edges to other (possibly new) states + while (!queue.isEmpty()) // Appel says: "until E and T did not change in this iteration". + // but: do we really need E here? I ignored it + { + // handle next state in queue + LALR1CPState stateKernel = queue.iterator().next(); + queue.remove(stateKernel); + + // debug messages + printDebugMessages(); + + // first, create a LALR(1)-with-CP-Links automaton and remember the closures + // for performance reasons + LALR1CPState state = kernel2closure.get(stateKernel); + Set shiftedSymbols = new HashSet(); + for (LALR1CPItem item : state.getItems()) { + // remember the state this item belongs to + itemStates.put(item, state); + // try to shift + if (item.isShiftable()) { + Symbol symbol = item.getNextSymbol(); + if (symbol == EndOfInputStream) { + // $-symbol: here we accept + lr0Edges.add(createAcceptEdge(stateKernel, symbol)); // GOON: with or without closure? + } else if (shiftedSymbols.add(symbol)) // shift each symbol only once { - //handle next state in queue - LALR1CPState stateKernel = queue.iterator().next(); - queue.remove(stateKernel); - - //debug messages - printDebugMessages(); - - //first, create a LALR(1)-with-CP-Links automaton and remember the closures - //for performance reasons - LALR1CPState state = kernel2closure.get(stateKernel); - Set shiftedSymbols = new HashSet(); - for (LALR1CPItem item : state.getItems()) { - //remember the state this item belongs to - itemStates.put(item, state); - //try to shift - if (item.isShiftable()) { - Symbol symbol = item.getNextSymbol(); - if (symbol == EndOfInputStream) { - //$-symbol: here we accept - lr0Edges.add(createAcceptEdge(stateKernel, symbol)); //GOON: with or without closure? - } else if (shiftedSymbols.add(symbol)) //shift each symbol only once - { - //terminal or non-terminal - - //shift to other state - Tuple2> s = state.goToCP(symbol); - LALR1CPState shiftedStateKernel = s.get1(); - List shiftedStateCPLinks = s.get2(); - - //we try to find out if there is already some state which has an equal - //kernel to the shifted state (LALR1CPState equals on kernel) - LALR1CPState equalStateLALR1CP = kernel2closure.get(shiftedStateKernel); - LALR1CPState gotoLinkTargetState = equalStateLALR1CP; - if (equalStateLALR1CP == null) { - //add new state - LALR1CPState shiftedState = shiftedStateKernel.closure(grammarInfo); - kernel2closure.put(shiftedStateKernel, shiftedState); - queue.add(shiftedStateKernel); - gotoLinkTargetState = shiftedState; - - } - - //remember CP links (cp link contains closure of target state, not only kernel) - for (CPGoToLink link : shiftedStateCPLinks) { - LALR1CPItem todoItem = link.getSource(); - if (goToLinks.containsKey(todoItem)) { - throw new RuntimeException("Double gotoLink!"); - } - goToLinks.put(todoItem, link.withTargetState(gotoLinkTargetState)); - } - - //add edge - lr0Edges.add(new Edge(stateKernel, symbol, shiftedStateKernel, item.getLR0Item())); - } - } - } - } - } - - if (debug) { - long actual = System.currentTimeMillis(); - start = actual - start; - System.out.println("LR(0) Generierung: " + (start / 1000.0)); - start = actual; - } - - Map lookaheads = map(); - - // Iterative depth-first search through the CP-graph for SCCs - { - // stack to keep track of path through the item-cp-graph - Stack dfsStack = stack(); - Stack sccStack = stack(); - Set sccSet = set(); - Set> edges = set(); - // DFS search and index - int dfsIndex = 0; - Map dfsIndices = map(); - Map lowlink = map(); - Map sccRoots = map(); - // set to keep track whether all nodes were touched - Set dfsSet = set(); - Set visited = set(); - for (LALR1CPState sta : kernel2closure.values()) { - Collection col = sta.getItemsAsCollection(); - for (LALR1CPItem it : col) { - EfficientTerminalSet la = it.getLookaheads(); - lookaheads.put(it, la); - dfsSet.add(it); + // terminal or non-terminal + + // shift to other state + Tuple2> s = state.goToCP(symbol); + LALR1CPState shiftedStateKernel = s.get1(); + List shiftedStateCPLinks = s.get2(); + + // we try to find out if there is already some state which has an equal + // kernel to the shifted state (LALR1CPState equals on kernel) + LALR1CPState equalStateLALR1CP = kernel2closure.get(shiftedStateKernel); + LALR1CPState gotoLinkTargetState = equalStateLALR1CP; + if (equalStateLALR1CP == null) { + // add new state + LALR1CPState shiftedState = shiftedStateKernel.closure(grammarInfo); + kernel2closure.put(shiftedStateKernel, shiftedState); + queue.add(shiftedStateKernel); + gotoLinkTargetState = shiftedState; + } + + // remember CP links (cp link contains closure of target state, not only kernel) + for (CPGoToLink link : shiftedStateCPLinks) { + LALR1CPItem todoItem = link.getSource(); + if (goToLinks.containsKey(todoItem)) { + throw new RuntimeException("Double gotoLink!"); } + goToLinks.put(todoItem, link.withTargetState(gotoLinkTargetState)); + } + // add edge + lr0Edges.add(new Edge(stateKernel, symbol, shiftedStateKernel, item.getLR0Item())); } + } + } + } + } - // initialise dfsStack with startstate's item - LALR1CPState startstate = kernel2closure.get(startStateKernel); - LALR1CPItem firstItem = startstate.getItemWithLookaheadByLR0Item(startStateKernelItem); - dfsStack.push(firstItem); - sccStack.push(firstItem); - sccSet.add(firstItem); - dfsIndices.put(firstItem, 0); - lowlink.put(firstItem, 0); - LALR1CPItem found; - - // iterate through the graph until we touched all states - // also compute SCC-internal LA-propagation for SCC - do { - dfsSet.remove(dfsStack.peek()); - - while (!(dfsStack.isEmpty())) { - found = null; - LALR1CPItem item = dfsStack.peek(); - // visit all links outgoing from item - if (visited.add(item)) { - CPGoToLink gotoLink = goToLinks.get(item); - int ll = lowlink.get(item); - if (gotoLink != null) { - LALR1CPState targetState = gotoLink.getTargetState(); - LALR1CPItem targetItem = targetState.getItemWithLookaheadByLR0Item(gotoLink.getTargetItem()); - if (dfsSet.contains(targetItem)) { - found = targetItem; - dfsStack.push(found); - sccStack.push(found); - sccSet.add(found); - dfsSet.remove(found); - dfsIndex++; - dfsIndices.put(found, dfsIndex); - lowlink.put(found, dfsIndex); - - } else { - if (sccSet.contains(targetItem)) { - lowlink.put(item, Math.min(ll, dfsIndices.get(targetItem))); - } - } - edges.add(t(item, targetItem)); - } - for (LR0Item closureLink : item.getClosureLinks()) { - LALR1CPState ownState = itemStates.get(item); //same state as current item - LALR1CPItem targetItem = ownState.getItemWithLookaheadByLR0Item(closureLink); - if (dfsSet.contains(targetItem)) { - found = targetItem; - dfsStack.push(found); - sccStack.push(found); - sccSet.add(found); - dfsSet.remove(found); - dfsIndex++; - dfsIndices.put(found, dfsIndex); - lowlink.put(found, dfsIndex); - } else { - if (sccSet.contains(targetItem)) { - lowlink.put(item, Math.min(ll, dfsIndices.get(targetItem))); - } - } - edges.add(t(item, targetItem)); - } - } - if (found == null) { // not found a child - dfsStack.pop(); // ascend - if (!dfsStack.isEmpty()) { - lowlink.put(dfsStack.peek(), Math.min(lowlink.get(dfsStack.peek()), lowlink.get(item))); - } - - if (lowlink.get(item).equals(dfsIndices.get(item))) { - sccRoots.put(dfsIndices.get(item), item); - Collection l = llist(); - EfficientTerminalSet ts = lookaheads.get(item); - l.add(item); - while ((found = sccStack.pop()) != item) { - lowlink.put(found, dfsIndices.get(item)); - ts = ts.plusAll(lookaheads.get(found)); - l.add(found); - sccSet.remove(found); - } - lookaheads.put(item, ts); - sccSet.remove(item); - } - } - - - } - - - - LALR1CPItem item = null; - LALR1CPItem backup = null; - for (LALR1CPItem it : dfsSet) { - backup = it; - if (it.getPosition() == 0) { - item = it; - break; - } - } - if (item == null && backup == null) { - break; - } else { - item = backup; - } + if (debug) { + long actual = System.currentTimeMillis(); + start = actual - start; + System.out.println("LR(0) Generierung: " + (start / 1000.0)); + start = actual; + } - dfsStack.push(item); - sccStack.push(item); - sccSet.add(item); + Map lookaheads = map(); + + // Iterative depth-first search through the CP-graph for SCCs + { + // stack to keep track of path through the item-cp-graph + Stack dfsStack = stack(); + Stack sccStack = stack(); + Set sccSet = set(); + Set> edges = set(); + // DFS search and index + int dfsIndex = 0; + Map dfsIndices = map(); + Map lowlink = map(); + Map sccRoots = map(); + // set to keep track whether all nodes were touched + Set dfsSet = set(); + Set visited = set(); + for (LALR1CPState sta : kernel2closure.values()) { + Collection col = sta.getItemsAsCollection(); + for (LALR1CPItem it : col) { + EfficientTerminalSet la = it.getLookaheads(); + lookaheads.put(it, la); + dfsSet.add(it); + } + } + + // initialise dfsStack with startstate's item + LALR1CPState startstate = kernel2closure.get(startStateKernel); + LALR1CPItem firstItem = startstate.getItemWithLookaheadByLR0Item(startStateKernelItem); + dfsStack.push(firstItem); + sccStack.push(firstItem); + sccSet.add(firstItem); + dfsIndices.put(firstItem, 0); + lowlink.put(firstItem, 0); + LALR1CPItem found; + + // iterate through the graph until we touched all states + // also compute SCC-internal LA-propagation for SCC + do { + dfsSet.remove(dfsStack.peek()); + + while (!(dfsStack.isEmpty())) { + found = null; + LALR1CPItem item = dfsStack.peek(); + // visit all links outgoing from item + if (visited.add(item)) { + CPGoToLink gotoLink = goToLinks.get(item); + int ll = lowlink.get(item); + if (gotoLink != null) { + LALR1CPState targetState = gotoLink.getTargetState(); + LALR1CPItem targetItem = + targetState.getItemWithLookaheadByLR0Item(gotoLink.getTargetItem()); + if (dfsSet.contains(targetItem)) { + found = targetItem; + dfsStack.push(found); + sccStack.push(found); + sccSet.add(found); + dfsSet.remove(found); dfsIndex++; - dfsIndices.put(item, dfsIndex); - lowlink.put(item, dfsIndex); - } while (!dfsStack.isEmpty()); - - if (debug) { - long actual = System.currentTimeMillis(); - start = actual - start; - System.out.println("Determining SCCs: " + (start / 1000.0)); - start = actual; - } - - - // found SCCs (lowlink(item) == SCC-id) - - - // now record SCC-graph-edges-relation (SCC-id -> 2^SCC-id) map) - // as well as SCC-incoming-count incoming(SCC-id) == count - - final Map incoming = map(); - Map> edgerelation = map(); - for (LALR1CPItem it : sccRoots.values()) { - incoming.put(it, 0); - edgerelation.put(it, new HashSet()); - } + dfsIndices.put(found, dfsIndex); + lowlink.put(found, dfsIndex); - for (Tuple2 e : edges) { - int source = lowlink.get(e.get1()); - int target = lowlink.get(e.get2()); - if (source == target) { - continue; + } else { + if (sccSet.contains(targetItem)) { + lowlink.put(item, Math.min(ll, dfsIndices.get(targetItem))); } - LALR1CPItem t = sccRoots.get(target); - if (edgerelation.get(sccRoots.get(source)).add(t)) { - incoming.put(t, incoming.get(t) + 1); - } - } - - if (debug) { - long actual = System.currentTimeMillis(); - start = actual - start; - System.out.println("Inter SCC-edges: " + (start / 1000.0)); - start = actual; + } + edges.add(t(item, targetItem)); } - - // compute Lookaheadpropagation in lookahead(SCC-id) - FibonacciHeap pq = new FibonacciHeap(); - for (LALR1CPItem it : sccRoots.values()) { - pq.insert(it, incoming.get(it)); - } - while (!pq.isEmpty()) { - LALR1CPItem it = pq.extractMin(); - EfficientTerminalSet itla = lookaheads.get(it); - for (LALR1CPItem t : edgerelation.get(it)) { - EfficientTerminalSet edgela = lookaheads.get(t); - pq.decrementKey(t); - lookaheads.put(t, edgela.plusAll(itla)); + for (LR0Item closureLink : item.getClosureLinks()) { + LALR1CPState ownState = itemStates.get(item); // same state as current item + LALR1CPItem targetItem = ownState.getItemWithLookaheadByLR0Item(closureLink); + if (dfsSet.contains(targetItem)) { + found = targetItem; + dfsStack.push(found); + sccStack.push(found); + sccSet.add(found); + dfsSet.remove(found); + dfsIndex++; + dfsIndices.put(found, dfsIndex); + lowlink.put(found, dfsIndex); + } else { + if (sccSet.contains(targetItem)) { + lowlink.put(item, Math.min(ll, dfsIndices.get(targetItem))); } + } + edges.add(t(item, targetItem)); } - if (debug) { - long actual = System.currentTimeMillis(); - start = actual - start; - System.out.println("Propagating LAs: " + (start / 1000.0)); - start = actual; + } + if (found == null) { // not found a child + dfsStack.pop(); // ascend + if (!dfsStack.isEmpty()) { + lowlink.put( + dfsStack.peek(), Math.min(lowlink.get(dfsStack.peek()), lowlink.get(item))); } - // refresh lookaheads of SCC-members - for (Map.Entry me : lowlink.entrySet()) { - lookaheads.put(me.getKey(), lookaheads.get(sccRoots.get(me.getValue()))); + if (Objects.equals(lowlink.get(item), dfsIndices.get(item))) { + sccRoots.put(dfsIndices.get(item), item); + Collection l = llist(); + EfficientTerminalSet ts = lookaheads.get(item); + l.add(item); + while ((found = sccStack.pop()) != item) { + lowlink.put(found, dfsIndices.get(item)); + ts = ts.plusAll(lookaheads.get(found)); + l.add(found); + sccSet.remove(found); + } + lookaheads.put(item, ts); + sccSet.remove(item); } + } } - - - - - - - //create states and edges from collected information - Map lalr1CPToLR1Map = map(); - for (LALR1CPState state : kernel2closure.keySet()) { - HashSet lr1Items = new HashSet(); - LALR1CPState stateWithClosure = kernel2closure.get(state); - - for (LR0Item strippedItem : state.getStrippedItems()) { - LALR1CPItem item = stateWithClosure.getItemWithLookaheadByLR0Item(strippedItem); - EfficientTerminalSet terminals = lookaheads.get(item); - lr1Items.add(new LR1Item(strippedItem, terminals)); - } - LR1State lr1State = new LR1State(lr1Items); - lalr1CPToLR1Map.put(state, lr1State); - dfaStates.add(lr1State); + LALR1CPItem item = null; + LALR1CPItem backup = null; + for (LALR1CPItem it : dfsSet) { + backup = it; + if (it.getPosition() == 0) { + item = it; + break; + } + } + if (item == null && backup == null) { + break; + } else { + item = backup; } - //fill dfaEdges - for (Edge edge : lr0Edges) { - this.dfaEdges.add(new Edge(lalr1CPToLR1Map.get(edge.getSrc()), - edge.getSymbol(), lalr1CPToLR1Map.get(edge.getDest()), edge.getSrcItem())); + dfsStack.push(item); + sccStack.push(item); + sccSet.add(item); + dfsIndex++; + dfsIndices.put(item, dfsIndex); + lowlink.put(item, dfsIndex); + } while (!dfsStack.isEmpty()); + + if (debug) { + long actual = System.currentTimeMillis(); + start = actual - start; + System.out.println("Determining SCCs: " + (start / 1000.0)); + start = actual; + } + + // found SCCs (lowlink(item) == SCC-id) + + // now record SCC-graph-edges-relation (SCC-id -> 2^SCC-id) map) + // as well as SCC-incoming-count incoming(SCC-id) == count + + final Map incoming = map(); + Map> edgerelation = map(); + for (LALR1CPItem it : sccRoots.values()) { + incoming.put(it, 0); + edgerelation.put(it, new HashSet()); + } + + for (Tuple2 e : edges) { + int source = lowlink.get(e.get1()); + int target = lowlink.get(e.get2()); + if (source == target) { + continue; + } + LALR1CPItem t = sccRoots.get(target); + if (edgerelation.get(sccRoots.get(source)).add(t)) { + incoming.put(t, incoming.get(t) + 1); } + } + + if (debug) { + long actual = System.currentTimeMillis(); + start = actual - start; + System.out.println("Inter SCC-edges: " + (start / 1000.0)); + start = actual; + } + + // compute Lookaheadpropagation in lookahead(SCC-id) + FibonacciHeap pq = new FibonacciHeap(); + for (LALR1CPItem it : sccRoots.values()) { + pq.insert(it, incoming.get(it)); + } + while (!pq.isEmpty()) { + LALR1CPItem it = pq.extractMin(); + EfficientTerminalSet itla = lookaheads.get(it); + for (LALR1CPItem t : edgerelation.get(it)) { + EfficientTerminalSet edgela = lookaheads.get(t); + pq.decrementKey(t); + lookaheads.put(t, edgela.plusAll(itla)); + } + } + if (debug) { + long actual = System.currentTimeMillis(); + start = actual - start; + System.out.println("Propagating LAs: " + (start / 1000.0)); + start = actual; + } + + // refresh lookaheads of SCC-members + for (Map.Entry me : lowlink.entrySet()) { + lookaheads.put(me.getKey(), lookaheads.get(sccRoots.get(me.getValue()))); + } + } - printDebugResult(); + // create states and edges from collected information + Map lalr1CPToLR1Map = map(); + for (LALR1CPState state : kernel2closure.keySet()) { + HashSet lr1Items = new HashSet(); + LALR1CPState stateWithClosure = kernel2closure.get(state); + + for (LR0Item strippedItem : state.getStrippedItems()) { + LALR1CPItem item = stateWithClosure.getItemWithLookaheadByLR0Item(strippedItem); + EfficientTerminalSet terminals = lookaheads.get(item); + lr1Items.add(new LR1Item(strippedItem, terminals)); + } + LR1State lr1State = new LR1State(lr1Items); + lalr1CPToLR1Map.put(state, lr1State); + dfaStates.add(lr1State); + } - return ret; + // fill dfaEdges + for (Edge edge : lr0Edges) { + this.dfaEdges.add( + new Edge( + lalr1CPToLR1Map.get(edge.getSrc()), + edge.getSymbol(), + lalr1CPToLR1Map.get(edge.getDest()), + edge.getSrcItem())); } + + printDebugResult(); + + return ret; + } } diff --git a/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/generator/LR0ParallelAutomatonFactory.java b/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/generator/LR0ParallelAutomatonFactory.java index b239e2f0b..3ca613e6e 100755 --- a/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/generator/LR0ParallelAutomatonFactory.java +++ b/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/generator/LR0ParallelAutomatonFactory.java @@ -2,150 +2,154 @@ import static edu.tum.cup2.grammar.SpecialTerminals.EndOfInputStream; +import edu.tum.cup2.generator.exceptions.GeneratorException; +import edu.tum.cup2.generator.items.LR0Item; +import edu.tum.cup2.generator.states.LR0State; +import edu.tum.cup2.grammar.Symbol; import java.util.LinkedList; +import java.util.Objects; import java.util.concurrent.Callable; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantLock; -import edu.tum.cup2.generator.exceptions.GeneratorException; -import edu.tum.cup2.generator.items.LR0Item; -import edu.tum.cup2.generator.states.LR0State; -import edu.tum.cup2.grammar.Symbol; - /** * Factory for a LR(0) automaton, that is computed concurrently. - * + * * @author Daniel Altmann * @author Michael Hausmann * @author Johannes Schamburger - * */ -public class LR0ParallelAutomatonFactory extends - AutomatonFactory { - - private class WorkerTaskLR0 extends WorkerTask { - - public WorkerTaskLR0(LR0State state, ThreadPoolExecutor threadPool, ReentrantLock lock) { - super(state, threadPool, lock); - } - - /** - * Handles one state; creates new tasks by shifting. - * @return 0 - we don't care about the return value, but the {@link Callable} - * interface forces us to return an Integer. - */ - @Override public Integer call() { - - // for all states, find their edges to other (possibly new) states - // Appel says: "until E and T did not change in this iteration". - // but: do we really need E here? I ignored it - - // debug messages - printDebugMessages(); - - currentState = (LR0State) stateKernel.closure(grammarInfo); // unpack state (from kernel to closure) - - for (LR0Item item : currentState.getItems()) { - - if (item.isShiftable()) { - Symbol symbol = item.getNextSymbol(); - if (symbol == EndOfInputStream) { - // $-symbol: here we accept - dfaEdges.add(Edge.createAcceptEdge(stateKernel, symbol)); - } else { - // terminal or non-terminal - LR0State shiftedState = (LR0State) currentState.goTo(symbol); - // new state? - if (dfaStates.add(shiftedState)) { - threadPool.submit(new WorkerTaskLR0(shiftedState, threadPool, taskCountLock)); - } - // add the edge - dfaEdges.add(new Edge(stateKernel, symbol, shiftedState, item)); - - } /* end else */ - } /* end if */ - } /* end for */ - - decrementTaskCount(); - - return 0; - - } - - } - - /** - * Creates a LR(0) automaton concurrently. - * @param generator - * @param grammarInfo - * @param numThreads the number of threads that will be used for the creation. - * @return the generated automaton. - * @throws GeneratorException - */ - public Automaton createAutomaton( - LRGenerator generator, GrammarInfo grammarInfo, int numThreads) - throws GeneratorException { - this.generator = generator; - this.grammarInfo = grammarInfo; - this.numThreads = numThreads; - - initCreation(); - - threadPool = new ThreadPoolExecutor(numThreads, numThreads, Long.MAX_VALUE, TimeUnit.NANOSECONDS, new LinkedBlockingQueue()); - taskCountLock = new ReentrantLock(); - - // create a new thread that waits until all tasks are done - shutDownThread = new Thread() { - @Override public void run() { - try { - synchronized(threadPool) { - threadPool.wait(); - } - } catch (InterruptedException e) { - e.printStackTrace(); - } - threadPool.shutdown(); - } - }; - shutDownThread.start(); - - // we need to ensure that the shutDownThread is already waiting to be notified - while(!(shutDownThread.getState()).equals(Thread.State.WAITING)) { - } - - taskCountLock.lock(); - try { - taskCount = 0; - } finally { - taskCountLock.unlock(); - } - // add one task for state0 to the thread pool and start the thread pool - LinkedList tasks = new LinkedList(); - tasks.add(new WorkerTaskLR0(state0, threadPool, taskCountLock)); - try { - threadPool.invokeAll(tasks); - } catch (Exception e) { - e.printStackTrace(); - } - - // wait until the ThreadPool has been shut down - try { - shutDownThread.join(); - } catch (InterruptedException e) { - e.printStackTrace(); - } - printDebugResult(); - - return ret; - } - - @Override - public Automaton createAutomaton( - LRGenerator generator, GrammarInfo grammarInfo) - throws GeneratorException { - return createAutomaton(generator, grammarInfo, defaultNumThreads); - } - +public class LR0ParallelAutomatonFactory extends AutomatonFactory { + + private class WorkerTaskLR0 extends WorkerTask { + + public WorkerTaskLR0(LR0State state, ThreadPoolExecutor threadPool, ReentrantLock lock) { + super(state, threadPool, lock); + } + + /** + * Handles one state; creates new tasks by shifting. + * + * @return 0 - we don't care about the return value, but the {@link Callable} interface forces + * us to return an Integer. + */ + @Override + public Integer call() { + + // for all states, find their edges to other (possibly new) states + // Appel says: "until E and T did not change in this iteration". + // but: do we really need E here? I ignored it + + // debug messages + printDebugMessages(); + + currentState = + (LR0State) stateKernel.closure(grammarInfo); // unpack state (from kernel to closure) + + for (LR0Item item : currentState.getItems()) { + + if (item.isShiftable()) { + Symbol symbol = item.getNextSymbol(); + if (symbol == EndOfInputStream) { + // $-symbol: here we accept + dfaEdges.add(Edge.createAcceptEdge(stateKernel, symbol)); + } else { + // terminal or non-terminal + LR0State shiftedState = (LR0State) currentState.goTo(symbol); + // new state? + if (dfaStates.add(shiftedState)) { + threadPool.submit(new WorkerTaskLR0(shiftedState, threadPool, taskCountLock)); + } + // add the edge + dfaEdges.add(new Edge(stateKernel, symbol, shiftedState, item)); + } /* end else */ + } /* end if */ + } /* end for */ + + decrementTaskCount(); + + return 0; + } + } + + /** + * Creates a LR(0) automaton concurrently. + * + * @param generator + * @param grammarInfo + * @param numThreads the number of threads that will be used for the creation. + * @return the generated automaton. + * @throws GeneratorException + */ + public Automaton createAutomaton( + LRGenerator generator, GrammarInfo grammarInfo, int numThreads) + throws GeneratorException { + this.generator = generator; + this.grammarInfo = grammarInfo; + this.numThreads = numThreads; + + initCreation(); + + threadPool = + new ThreadPoolExecutor( + numThreads, + numThreads, + Long.MAX_VALUE, + TimeUnit.NANOSECONDS, + new LinkedBlockingQueue()); + taskCountLock = new ReentrantLock(); + + // create a new thread that waits until all tasks are done + shutDownThread = + new Thread() { + @Override + public void run() { + try { + synchronized (threadPool) { + threadPool.wait(); + } + } catch (InterruptedException e) { + e.printStackTrace(); + } + threadPool.shutdown(); + } + }; + shutDownThread.start(); + + // we need to ensure that the shutDownThread is already waiting to be notified + while (!Objects.equals(shutDownThread.getState(), Thread.State.WAITING)) {} + + taskCountLock.lock(); + try { + taskCount = 0; + } finally { + taskCountLock.unlock(); + } + // add one task for state0 to the thread pool and start the thread pool + LinkedList tasks = new LinkedList(); + tasks.add(new WorkerTaskLR0(state0, threadPool, taskCountLock)); + try { + threadPool.invokeAll(tasks); + } catch (Exception e) { + e.printStackTrace(); + } + + // wait until the ThreadPool has been shut down + try { + shutDownThread.join(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + printDebugResult(); + + return ret; + } + + @Override + public Automaton createAutomaton( + LRGenerator generator, GrammarInfo grammarInfo) throws GeneratorException { + return createAutomaton(generator, grammarInfo, defaultNumThreads); + } } /* end of AutomatonFactory */ diff --git a/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/generator/LR1ParallelAutomatonFactory.java b/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/generator/LR1ParallelAutomatonFactory.java index 18f10a4e7..623ef6f4c 100755 --- a/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/generator/LR1ParallelAutomatonFactory.java +++ b/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/generator/LR1ParallelAutomatonFactory.java @@ -2,152 +2,152 @@ import static edu.tum.cup2.grammar.SpecialTerminals.EndOfInputStream; +import edu.tum.cup2.generator.exceptions.GeneratorException; +import edu.tum.cup2.generator.items.*; +import edu.tum.cup2.generator.states.*; +import edu.tum.cup2.grammar.Symbol; import java.util.LinkedList; +import java.util.Objects; import java.util.concurrent.Callable; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantLock; -import edu.tum.cup2.generator.exceptions.GeneratorException; -import edu.tum.cup2.generator.items.*; -import edu.tum.cup2.generator.states.*; -import edu.tum.cup2.grammar.Symbol; - /** * Factory for a LR(1) automaton, that is computed concurrently. - * + * * @author Daniel Altmann * @author Michael Hausmann * @author Johannes Schamburger - * */ -public class LR1ParallelAutomatonFactory extends AutomatonFactory -{ - private class WorkerTaskLR1 extends WorkerTask { - - public WorkerTaskLR1(LR1State state, ThreadPoolExecutor threadPool, ReentrantLock lock) { - super(state, threadPool, lock); - } - - /** - * Handles one state; creates new tasks by shifting. - * @return 0 - we don't care about the return value, but the {@link Callable} - * interface forces us to return an Integer. - */ - public Integer call() { - - // for all states, find their edges to other (possibly new) states - // Appel says: "until E and T did not change in this iteration". - // but: do we really need E here? I ignored it -// System.out.println(Thread.currentThread() + " working on... " + stateKernel); - - // debug messages - printDebugMessages(); - - currentState = stateKernel.closure(grammarInfo); // unpack state (from kernel to closure) - - for (LR1Item item : currentState.getItems()) { - - if (item.isShiftable()) { - Symbol symbol = item.getNextSymbol(); - if (symbol == EndOfInputStream) { - // $-symbol: here we accept - dfaEdges.add(Edge.createAcceptEdge(stateKernel, symbol)); - } else { - // terminal or non-terminal - LR1State shiftedState = currentState.goTo(symbol); - // new state? - if (dfaStates.add(shiftedState)) { - threadPool.submit(new WorkerTaskLR1(shiftedState, threadPool, taskCountLock)); - } - // add the edge - dfaEdges.add(new Edge(stateKernel, symbol, shiftedState, item.getLR0Kernel())); - - } /* end else */ - } /* end if */ - } /* end for */ - - decrementTaskCount(); - - return 0; - - } - - - } - - /** - * Creates a LR(1) automaton concurrently. - * @param generator - * @param grammarInfo - * @param numThreads the number of threads that will be used for the creation. - * @return the generated automaton. - * @throws GeneratorException - */ - public Automaton createAutomaton( - LRGenerator generator, GrammarInfo grammarInfo, int numThreads) - throws GeneratorException { - this.generator = generator; - this.grammarInfo = grammarInfo; - this.numThreads = numThreads; - - initCreation(); - - threadPool = new ThreadPoolExecutor(numThreads, numThreads, Long.MAX_VALUE, TimeUnit.NANOSECONDS, new LinkedBlockingQueue()); - taskCountLock = new ReentrantLock(); - - // create a new thread that waits until all tasks are done - shutDownThread = new Thread() { - @Override public void run() { - try { - synchronized(threadPool) { - threadPool.wait(); - } - } catch (InterruptedException e) { - e.printStackTrace(); - } - threadPool.shutdown(); - } - }; - shutDownThread.start(); - - // we need to ensure that the shutDownThread is already waiting to be notified - while(!(shutDownThread.getState()).equals(Thread.State.WAITING)) { - } - - taskCountLock.lock(); - try { - taskCount = 0; - } finally { - taskCountLock.unlock(); - } - // add one task for state0 to the thread pool and start the thread pool - LinkedList tasks = new LinkedList(); - tasks.add(new WorkerTaskLR1(state0, threadPool, taskCountLock)); - try { - threadPool.invokeAll(tasks); - } catch (InterruptedException e) { - e.printStackTrace(); - } - - // wait until the ThreadPool has been shut down - try { - shutDownThread.join(); - } catch (InterruptedException e) { - e.printStackTrace(); - } - printDebugResult(); - - return ret; - } - - @Override - public Automaton createAutomaton( - LRGenerator generator, GrammarInfo grammarInfo) - throws GeneratorException { - return createAutomaton(generator, grammarInfo, defaultNumThreads); - } - - +public class LR1ParallelAutomatonFactory extends AutomatonFactory { + private class WorkerTaskLR1 extends WorkerTask { + + public WorkerTaskLR1(LR1State state, ThreadPoolExecutor threadPool, ReentrantLock lock) { + super(state, threadPool, lock); + } + + /** + * Handles one state; creates new tasks by shifting. + * + * @return 0 - we don't care about the return value, but the {@link Callable} interface forces + * us to return an Integer. + */ + public Integer call() { + + // for all states, find their edges to other (possibly new) states + // Appel says: "until E and T did not change in this iteration". + // but: do we really need E here? I ignored it + // System.out.println(Thread.currentThread() + " working on... " + stateKernel); + + // debug messages + printDebugMessages(); + + currentState = stateKernel.closure(grammarInfo); // unpack state (from kernel to closure) + + for (LR1Item item : currentState.getItems()) { + + if (item.isShiftable()) { + Symbol symbol = item.getNextSymbol(); + if (symbol == EndOfInputStream) { + // $-symbol: here we accept + dfaEdges.add(Edge.createAcceptEdge(stateKernel, symbol)); + } else { + // terminal or non-terminal + LR1State shiftedState = currentState.goTo(symbol); + // new state? + if (dfaStates.add(shiftedState)) { + threadPool.submit(new WorkerTaskLR1(shiftedState, threadPool, taskCountLock)); + } + // add the edge + dfaEdges.add(new Edge(stateKernel, symbol, shiftedState, item.getLR0Kernel())); + } /* end else */ + } /* end if */ + } /* end for */ + + decrementTaskCount(); + + return 0; + } + } + + /** + * Creates a LR(1) automaton concurrently. + * + * @param generator + * @param grammarInfo + * @param numThreads the number of threads that will be used for the creation. + * @return the generated automaton. + * @throws GeneratorException + */ + public Automaton createAutomaton( + LRGenerator generator, GrammarInfo grammarInfo, int numThreads) + throws GeneratorException { + this.generator = generator; + this.grammarInfo = grammarInfo; + this.numThreads = numThreads; + + initCreation(); + + threadPool = + new ThreadPoolExecutor( + numThreads, + numThreads, + Long.MAX_VALUE, + TimeUnit.NANOSECONDS, + new LinkedBlockingQueue()); + taskCountLock = new ReentrantLock(); + + // create a new thread that waits until all tasks are done + shutDownThread = + new Thread() { + @Override + public void run() { + try { + synchronized (threadPool) { + threadPool.wait(); + } + } catch (InterruptedException e) { + e.printStackTrace(); + } + threadPool.shutdown(); + } + }; + shutDownThread.start(); + + // we need to ensure that the shutDownThread is already waiting to be notified + while (!Objects.equals(shutDownThread.getState(), Thread.State.WAITING)) {} + + taskCountLock.lock(); + try { + taskCount = 0; + } finally { + taskCountLock.unlock(); + } + // add one task for state0 to the thread pool and start the thread pool + LinkedList tasks = new LinkedList(); + tasks.add(new WorkerTaskLR1(state0, threadPool, taskCountLock)); + try { + threadPool.invokeAll(tasks); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + // wait until the ThreadPool has been shut down + try { + shutDownThread.join(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + printDebugResult(); + + return ret; + } + + @Override + public Automaton createAutomaton( + LRGenerator generator, GrammarInfo grammarInfo) throws GeneratorException { + return createAutomaton(generator, grammarInfo, defaultNumThreads); + } } /*end of AutomatonFactory*/ diff --git a/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/generator/LRGenerator.java b/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/generator/LRGenerator.java index d57d9a669..49c67966e 100644 --- a/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/generator/LRGenerator.java +++ b/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/generator/LRGenerator.java @@ -1,11 +1,5 @@ package edu.tum.cup2.generator; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; - import edu.tum.cup2.generator.exceptions.GeneratorException; import edu.tum.cup2.generator.exceptions.ReduceReduceConflict; import edu.tum.cup2.generator.exceptions.ShiftReduceConflict; @@ -32,412 +26,369 @@ import edu.tum.cup2.precedences.RightAssociativity; import edu.tum.cup2.spec.CUP2Specification; import edu.tum.cup2.util.It; - +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Objects; /** - * Base class for the LR(0) and LR(1) parser generator. - * Contains the common code of both generators. - * + * Base class for the LR(0) and LR(1) parser generator. Contains the common code of both generators. + * * @author Andreas Wenger * @author Stefan Dangl */ -public abstract class LRGenerator> - extends Generator -{ - - protected final GrammarInfo grammarInfo; - private final LRParsingTable parsingTable; - private Automaton automaton; - protected static int numThreads = 4; - - /** - * Computes a {@link LRParsingTable} for the given LR grammar. - * The given verbosity tells the generator how many debug messages are requested. - * - * For a description of this algorithm, see Appel's book, pages 62 to 66. - */ - //public LRGenerator(Grammar grammar, Precedences precedences, Verbosity verbosity, boolean extendGrammar) - public LRGenerator(CUP2Specification spec, final Verbosity verbosity, boolean extendGrammar) - throws GeneratorException - { - //extend grammar by "S' → S$" production - super(extendGrammar ? spec.getGrammar().extendByAuxStartProduction() : spec.getGrammar(), spec.getPrecedences(), verbosity); - - //compute additional information about the grammar - grammarInfo = new GrammarInfo(grammar); - - //construct the DFA - Automaton dfa = createAutomaton(); - automaton = dfa; //cache it - S state0 = dfa.getStartState(); - - //construct the parsing tables - - //create empty tables - LRParsingTable table = new LRParsingTable(grammar, spec.getParserInterface(), dfa.getStates().size()); - LRActionTable actionTable = table.getActionTable(); - LRGoToTable goToTable = table.getGotoTable(); - - //create a mapping from our complicated LR parser generator states - //to the simple states of the LR parser. the order of the states does not matter, - //but we have to pay attention that the start state has index 0. - HashMap statesMap = new HashMap(); - It tableStates = table.getStates(); - statesMap.put(state0, tableStates.next()); //start state - for (S dfaState : dfa.getStates()) - { - if (!dfaState.equals(state0)) - statesMap.put(dfaState, tableStates.next()); //other state - } - - //create shift, goto and accept actions - for (Edge edge : dfa.getEdges()) - { - LRParserState src = statesMap.get(edge.getSrc()); - if (edge.getSymbol() instanceof Terminal) - { - Terminal t = (Terminal) edge.getSymbol(); - if (edge.isDestAccepting()) - { - //accept - actionTable.set(new Accept(), src, t); - } - else - { - //shift - actionTable.set(new Shift(statesMap.get(edge.getDest()), - edge.getSrcItem().getProduction(), edge.getSrcItem().getPosition() + 1), src, t); - } - } - else if (edge.getSymbol() instanceof NonTerminal) - { - //goto - goToTable.set(statesMap.get(edge.getDest()), src, (NonTerminal) edge.getSymbol()); - } - } - - //create the reduce actions - for (LRState stateKernel : statesMap.keySet()) //for each generator state - { - LRState state = stateKernel.closure(grammarInfo); //unpack state (from kernel to closure) - //if any item is not shiftable any more, reduce it to the left hand side of its production - for (I item : state.getItems()) - { - if (!item.isShiftable()) - { - try { - createReduceActions(actionTable, item, statesMap.get(stateKernel)); - } catch (GeneratorException c) { - //String word = getMinWordForState(stateKernel,dfa,state0, new HashSet>()); - String word = getShortestWord4State(stateKernel,state0); - // word may be null if state can not be reached from state0 - // (should never occur if automaton is calculated correctly) - if (word!=null){ - if (c instanceof ShiftReduceConflict) - ((ShiftReduceConflict)c).setWord(word.toString()); - if (c instanceof ReduceReduceConflict) - ((ReduceReduceConflict)c).setWord(word.toString()); - } - throw c; // TODO : Tell java-developers to provide rethrow!! - } - } - } - } - - //finished :-) - this.parsingTable = table; - - //TODO - if (verbosity != Verbosity.None) - { - debugOut.println("Result: Unique parse states: " + table.getStatesCount()); - } - - } - /** new algorithm - * - traversing automaton backwards - * - uses broad search of length N to find a word - * - working set consists of words of length == M - * - processed set contains words of length <= M - **/ - - private final boolean LOG_shortestWordFinder = false; - - private String getShortestWord4State( State crashState, S state0 ) - { - if (crashState==state0) - return ""; - HashMap, LinkedList> workingSet = new HashMap,LinkedList>(); - LinkedList> workingSetSortedKeyList = new LinkedList>(); - HashSet> processedStates = new HashSet>(); - workingSet.put(crashState, new LinkedList()); - workingSetSortedKeyList.add(crashState); - while (true) - { - State currentState = workingSetSortedKeyList.removeFirst(); - processedStates.add(currentState); - LinkedList currentWord = workingSet.remove(currentState); - List currentEdges = automaton.getEdgeTo(currentState); - if (LOG_shortestWordFinder){ - System.out.println("\t\t---"); - System.out.println("current word : "+currentWord); - System.out.println("current state : "+currentState/*.closure(grammarInfo)*/); - System.out.println("working-set size : "+workingSetSortedKeyList.size()); - System.out.println("number of edges : "+currentEdges.size()); - } - nextEdge: for (Edge currentEdge : currentEdges ) - { - State nextState = currentEdge.getSrc(); - // we already processed this state with a word of length <=M - // or will process it with a word of length of M? - // currently we are creating a word of length M+1 for this state - // so our word is not so good in this case :( - if (workingSet.containsKey(nextState) || processedStates.contains(nextState)) - { - if (LOG_shortestWordFinder){ - System.out.println(nextState.hashCode()+" already visited or in list of states to be visited .."); - } - continue nextEdge; // TODO : what about epsilon - } - - // yay, we found the shortest word for this state - LinkedList nextWord = new LinkedList(currentWord); - List symbolStrings = null; - Symbol symbol = currentEdge.getSymbol(); - if (symbol instanceof Terminal) - symbolStrings = Arrays.asList(symbol.toString()); - else if (symbol instanceof NonTerminal) { - symbolStrings = getShortestWordForNT((NonTerminal)symbol); - if (symbolStrings==null) - symbolStrings = Arrays.asList(symbol.toString()); - } else - symbolStrings = Arrays.asList("[unknown symbol-type]"); - nextWord.addAll(0,symbolStrings); - if (nextState==state0) { - String word = ""; - boolean first = true; - for (String s : nextWord){ - if (first) first = false; - else word+=" "; - word += s; - } - return word; - } - if (LOG_shortestWordFinder){ - System.out.println("Adding to working-set : "+nextState.hashCode()+" for word : "+nextWord); - } - workingSet.put(nextState, nextWord); - workingSetSortedKeyList.addLast(nextState); - } - } - } +public abstract class LRGenerator> extends Generator { + + protected final GrammarInfo grammarInfo; + private final LRParsingTable parsingTable; + private Automaton automaton; + protected static int numThreads = 4; + + /** + * Computes a {@link LRParsingTable} for the given LR grammar. The given verbosity tells the + * generator how many debug messages are requested. + * + *

For a description of this algorithm, see Appel's book, pages 62 to 66. + */ + // public LRGenerator(Grammar grammar, Precedences precedences, Verbosity verbosity, boolean + // extendGrammar) + public LRGenerator(CUP2Specification spec, final Verbosity verbosity, boolean extendGrammar) + throws GeneratorException { + // extend grammar by "S' → S$" production + super( + extendGrammar ? spec.getGrammar().extendByAuxStartProduction() : spec.getGrammar(), + spec.getPrecedences(), + verbosity); + + // compute additional information about the grammar + grammarInfo = new GrammarInfo(grammar); + + // construct the DFA + Automaton dfa = createAutomaton(); + automaton = dfa; // cache it + S state0 = dfa.getStartState(); + + // construct the parsing tables + + // create empty tables + LRParsingTable table = + new LRParsingTable(grammar, spec.getParserInterface(), dfa.getStates().size()); + LRActionTable actionTable = table.getActionTable(); + LRGoToTable goToTable = table.getGotoTable(); + + // create a mapping from our complicated LR parser generator states + // to the simple states of the LR parser. the order of the states does not matter, + // but we have to pay attention that the start state has index 0. + HashMap statesMap = new HashMap(); + It tableStates = table.getStates(); + statesMap.put(state0, tableStates.next()); // start state + for (S dfaState : dfa.getStates()) { + if (!Objects.equals(dfaState, state0)) + statesMap.put(dfaState, tableStates.next()); // other state + } + + // create shift, goto and accept actions + for (Edge edge : dfa.getEdges()) { + LRParserState src = statesMap.get(edge.getSrc()); + if (edge.getSymbol() instanceof Terminal) { + Terminal t = (Terminal) edge.getSymbol(); + if (edge.isDestAccepting()) { + // accept + actionTable.set(new Accept(), src, t); + } else { + // shift + actionTable.set( + new Shift( + statesMap.get(edge.getDest()), + edge.getSrcItem().getProduction(), + edge.getSrcItem().getPosition() + 1), + src, + t); + } + } else if (edge.getSymbol() instanceof NonTerminal) { + // goto + goToTable.set(statesMap.get(edge.getDest()), src, (NonTerminal) edge.getSymbol()); + } + } + + // create the reduce actions + for (LRState stateKernel : statesMap.keySet()) // for each generator state + { + LRState state = stateKernel.closure(grammarInfo); // unpack state (from kernel to closure) + // if any item is not shiftable any more, reduce it to the left hand side of its production + for (I item : state.getItems()) { + if (!item.isShiftable()) { + try { + createReduceActions(actionTable, item, statesMap.get(stateKernel)); + } catch (GeneratorException c) { + // String word = getMinWordForState(stateKernel,dfa,state0, new HashSet>()); + String word = getShortestWord4State(stateKernel, state0); + // word may be null if state can not be reached from state0 + // (should never occur if automaton is calculated correctly) + if (word != null) { + if (c instanceof ShiftReduceConflict) + ((ShiftReduceConflict) c).setWord(word.toString()); + if (c instanceof ReduceReduceConflict) + ((ReduceReduceConflict) c).setWord(word.toString()); + } + throw c; // TODO : Tell java-developers to provide rethrow!! + } + } + } + } + + // finished :-) + this.parsingTable = table; + + // TODO + if (verbosity != Verbosity.None) { + debugOut.println("Result: Unique parse states: " + table.getStatesCount()); + } + } + /** + * new algorithm - traversing automaton backwards - uses broad search of length N to find a word - + * working set consists of words of length == M - processed set contains words of length <= M + */ + private final boolean LOG_shortestWordFinder = false; + + private String getShortestWord4State(State crashState, S state0) { + if (crashState == state0) return ""; + HashMap, LinkedList> workingSet = new HashMap, LinkedList>(); + LinkedList> workingSetSortedKeyList = new LinkedList>(); + HashSet> processedStates = new HashSet>(); + workingSet.put(crashState, new LinkedList()); + workingSetSortedKeyList.add(crashState); + while (true) { + State currentState = workingSetSortedKeyList.removeFirst(); + processedStates.add(currentState); + LinkedList currentWord = workingSet.remove(currentState); + List currentEdges = automaton.getEdgeTo(currentState); + if (LOG_shortestWordFinder) { + System.out.println("\t\t---"); + System.out.println("current word : " + currentWord); + System.out.println("current state : " + currentState /*.closure(grammarInfo)*/); + System.out.println("working-set size : " + workingSetSortedKeyList.size()); + System.out.println("number of edges : " + currentEdges.size()); + } + nextEdge: + for (Edge currentEdge : currentEdges) { + State nextState = currentEdge.getSrc(); + // we already processed this state with a word of length <=M + // or will process it with a word of length of M? + // currently we are creating a word of length M+1 for this state + // so our word is not so good in this case :( + if (workingSet.containsKey(nextState) || processedStates.contains(nextState)) { + if (LOG_shortestWordFinder) { + System.out.println( + nextState.hashCode() + " already visited or in list of states to be visited .."); + } + continue nextEdge; // TODO : what about epsilon + } + + // yay, we found the shortest word for this state + LinkedList nextWord = new LinkedList(currentWord); + List symbolStrings = null; + Symbol symbol = currentEdge.getSymbol(); + if (symbol instanceof Terminal) symbolStrings = Arrays.asList(symbol.toString()); + else if (symbol instanceof NonTerminal) { + symbolStrings = getShortestWordForNT((NonTerminal) symbol); + if (symbolStrings == null) symbolStrings = Arrays.asList(symbol.toString()); + } else symbolStrings = Arrays.asList("[unknown symbol-type]"); + nextWord.addAll(0, symbolStrings); + if (nextState == state0) { + String word = ""; + boolean first = true; + for (String s : nextWord) { + if (first) first = false; + else word += " "; + word += s; + } + return word; + } + if (LOG_shortestWordFinder) { + System.out.println( + "Adding to working-set : " + nextState.hashCode() + " for word : " + nextWord); + } + workingSet.put(nextState, nextWord); + workingSetSortedKeyList.addLast(nextState); + } + } + } + + private List getShortestWordForNT(NonTerminal nt) { + return getShortestWordForNT(nt, new HashMap>()); + } + + private List getShortestWordForNT( + NonTerminal nt, HashMap> visited) { + LinkedList minNTWord = null; + if (visited.containsKey(nt)) // TODO : should never happen + { + System.err.println("getMinWordForNT algorithm does things that should not happen!"); + List word = visited.get(nt); + if (word == null) + throw new RuntimeException( + "getMinWordForNT algorithm is not behaving to it's implementor's satisfaction :?"); + else return word; + } + visited.put(nt, null); + FOR_PRODUCTION: + for (Production p : grammar.getProductions()) { + if (p.getLHS() == nt) { + LinkedList NTWord = new LinkedList(); + for (Symbol s : p.getRHS()) { + if (s instanceof Terminal) { + if (s != SpecialTerminals.Epsilon) NTWord.add(s.toString()); + } else if (s instanceof NonTerminal) { + NonTerminal nt2 = (NonTerminal) s; + // if the non-terminal is being visited in recursive call, + // do not use this production + // otherwise if the non-terminal has already been visited, take the + // minimal word which has been calculated before. + if (visited.containsKey(nt2)) { + List got = visited.get(nt2); + if (got == null) continue FOR_PRODUCTION; + else + NTWord.addAll( + got); // TODO: is this word truly minimal??? -> counter-example of grammar! + } else { + // nt2 is not currently processed and no minimal word is known for it + // thus we calculate the minimal word. + List minWord4S = getShortestWordForNT(nt2, visited); + if (minWord4S == null) { + // we were unable to calculate a word for nt2 + // because either nt2 is not productive + // or it uses a non-terminal which is already + // being processed + continue FOR_PRODUCTION; + } + NTWord.addAll(minWord4S); + } + } + } + if (minNTWord == null || minNTWord.size() > NTWord.size()) minNTWord = NTWord; + } + } + if (minNTWord == null) visited.remove(nt); + else visited.put(nt, minNTWord); + return minNTWord; + } + + /** + * Creates the DFA of the parser, consisting of states and edges (for shift and goto) connecting + * them. + */ + public abstract Automaton createAutomaton() throws GeneratorException; + /** + * Creates and returns the start state. This is the item, which consists of the start production + * at position 0. + */ + protected abstract S createStartState(); - private List getShortestWordForNT( NonTerminal nt ) { - return getShortestWordForNT(nt,new HashMap>()); - } - private List getShortestWordForNT( NonTerminal nt, HashMap> visited ) { - LinkedList minNTWord = null; - if (visited.containsKey(nt)) // TODO : should never happen - { - System.err.println("getMinWordForNT algorithm does things that should not happen!"); - List word = visited.get(nt); - if (word==null) - throw new RuntimeException("getMinWordForNT algorithm is not behaving to it's implementor's satisfaction :?"); - else - return word; - } - visited.put(nt,null); - FOR_PRODUCTION : for (Production p : grammar.getProductions()) { - if (p.getLHS()==nt) { - LinkedList NTWord = new LinkedList(); - for (Symbol s : p.getRHS()) { - if (s instanceof Terminal) { - if (s!=SpecialTerminals.Epsilon) - NTWord.add(s.toString()); - } else if (s instanceof NonTerminal) { - NonTerminal nt2 = (NonTerminal)s; - // if the non-terminal is being visited in recursive call, - // do not use this production - // otherwise if the non-terminal has already been visited, take the - // minimal word which has been calculated before. - if (visited.containsKey(nt2)) { - List got = visited.get(nt2); - if (got==null) - continue FOR_PRODUCTION; - else - NTWord.addAll(got); // TODO: is this word truly minimal??? -> counter-example of grammar! - } else { - // nt2 is not currently processed and no minimal word is known for it - // thus we calculate the minimal word. - List minWord4S = getShortestWordForNT(nt2, visited); - if (minWord4S==null) { - // we were unable to calculate a word for nt2 - // because either nt2 is not productive - // or it uses a non-terminal which is already - // being processed - continue FOR_PRODUCTION; - } - NTWord.addAll(minWord4S); - } - } - } - if (minNTWord==null || minNTWord.size()>NTWord.size()) - minNTWord = NTWord; - } - } - if (minNTWord==null) - visited.remove(nt); - else - visited.put(nt, minNTWord); - return minNTWord; - } + /** + * Fills the given {@link LRActionTable} with reduce actions, using the given non-shiftable item + * and its parent parser state. + */ + protected abstract void createReduceActions( + LRActionTable actionTable, I item, LRParserState state) throws GeneratorException; + /** + * Sets the given {@link Reduce} action to the given {@link LRActionTable} at the given position. + * If that cell is already filled and the conflict can not be solved by the precedences, an + * appropriate exception is thrown. + */ + protected void setReduceAction( + LRActionTable actionTable, Reduce reduce, LRParserState atState, Terminal atTerminal) + throws GeneratorException { + // check, if the cells is already filled with a shift or reduction. if so, + // we have a conflict (which can be resolved when there are appropriate precedences) + LRAction action = actionTable.getWithNull(atState, atTerminal); + if (action != null) { + if (action instanceof Shift) { + // try to find out if production or terminal has higher precedence + int prec = 0; + if (precedences != null) { + prec = precedences.compare(reduce.getProduction(), atTerminal); + if (prec == 1) { + // production has higher precedence, hence reduce + actionTable.set(reduce, atState, atTerminal); + } else if (prec == -1) { + // terminal has higher precedence, hence shift + // do not change current cell + } else { + // equal precedence. if associativity is left, then reduce, if it is right, + // then shift, else it is a conflict + Associativity ass = precedences.getAssociativity(atTerminal); + if (ass instanceof LeftAssociativity) + actionTable.set(reduce, atState, atTerminal); // reduce + else if (ass instanceof RightAssociativity) + /* do not change the current cell */ ; // shift + else if (ass instanceof NonAssocAssociativity) { + /*two consecutive occurrences of equal precedence non-associative + terminals generates an error.*/ + Terminal crashTerminal = atTerminal; + if (atTerminal == SpecialTerminals.WholeRow) + crashTerminal = actionTable.getTerminalOfFirstShiftAction(atState); + // throw new ConsecutiveNonAssocException((Shift) action, reduce, crashTerminal); + // to be consistent with CUP, an in general to be more logical, we defer the error + // reporting to the runtime + actionTable.set(new ConsecutiveNonAssocAction(), atState, atTerminal); + } else { + Terminal crashTerminal = atTerminal; + if (atTerminal == SpecialTerminals.WholeRow) + crashTerminal = actionTable.getTerminalOfFirstShiftAction(atState); + throw new ShiftReduceConflict((Shift) action, reduce, crashTerminal); + } + } + } else { + Terminal crashTerminal = atTerminal; + if (atTerminal == SpecialTerminals.WholeRow) + crashTerminal = actionTable.getTerminalOfFirstShiftAction(atState); + throw new ShiftReduceConflict((Shift) action, reduce, crashTerminal); + } + } else if (action instanceof Reduce) { + // try to find out if production or terminal has higher precedence + Reduce reduce2 = (Reduce) action; + int prec = 0; + if (precedences != null) { + prec = + precedences.compare( + reduce.getProduction(), reduce2.getProduction().getPrecedenceTerminal()); + if (prec == 1) { + // reduction has higher precedence, hence reduce + actionTable.set(reduce, atState, atTerminal); + } else if (prec == -1) { + // reduction2 has higher precedence, hence shift + actionTable.set(reduce2, atState, atTerminal); + } else throw new ReduceReduceConflict(reduce, (Reduce) action); + } else throw new ReduceReduceConflict(reduce, (Reduce) action); - /** - * Creates the DFA of the parser, consisting of states and edges - * (for shift and goto) connecting them. - */ - public abstract Automaton createAutomaton() throws GeneratorException; - - - - /** - * Creates and returns the start state. - * This is the item, which consists of the start production at position 0. - */ - protected abstract S createStartState(); + } else throw new GeneratorException("Unknown conflict"); + } else { + // empty cell, no conflict + actionTable.set(reduce, atState, atTerminal); + } + } + /** Gets the resulting parsing table. */ + public LRParsingTable getParsingTable() { + return parsingTable; + } - /** - * Fills the given {@link LRActionTable} with reduce actions, using the - * given non-shiftable item and its parent parser state. - */ - protected abstract void createReduceActions(LRActionTable actionTable, I item, LRParserState state) - throws GeneratorException; - - - /** - * Sets the given {@link Reduce} action to the given {@link LRActionTable} at the given - * position. If that cell is already filled and the conflict can not be solved by - * the precedences, an appropriate exception is thrown. - */ - protected void setReduceAction(LRActionTable actionTable, Reduce reduce, - LRParserState atState, Terminal atTerminal) - throws GeneratorException - { - //check, if the cells is already filled with a shift or reduction. if so, - //we have a conflict (which can be resolved when there are appropriate precedences) - LRAction action = actionTable.getWithNull(atState, atTerminal); - if (action != null) - { - if (action instanceof Shift) - { - //try to find out if production or terminal has higher precedence - int prec = 0; - if (precedences != null) - { - prec = precedences.compare(reduce.getProduction(), atTerminal); - if (prec == 1) - { - //production has higher precedence, hence reduce - actionTable.set(reduce, atState, atTerminal); - } - else if (prec == -1) - { - //terminal has higher precedence, hence shift - //do not change current cell - } - else - { - //equal precedence. if associativity is left, then reduce, if it is right, - //then shift, else it is a conflict - Associativity ass = precedences.getAssociativity(atTerminal); - if (ass instanceof LeftAssociativity) - actionTable.set(reduce, atState, atTerminal); //reduce - else if (ass instanceof RightAssociativity) - /* do not change the current cell */; //shift - else if (ass instanceof NonAssocAssociativity) { - /*two consecutive occurrences of equal precedence non-associative - terminals generates an error.*/ - Terminal crashTerminal = atTerminal; - if (atTerminal==SpecialTerminals.WholeRow) - crashTerminal = actionTable.getTerminalOfFirstShiftAction(atState); - // throw new ConsecutiveNonAssocException((Shift) action, reduce, crashTerminal); - // to be consistent with CUP, an in general to be more logical, we defer the error reporting to the runtime - actionTable.set(new ConsecutiveNonAssocAction(), atState, atTerminal); - } else { - Terminal crashTerminal = atTerminal; - if (atTerminal==SpecialTerminals.WholeRow) - crashTerminal = actionTable.getTerminalOfFirstShiftAction(atState); - throw new ShiftReduceConflict((Shift) action, reduce, crashTerminal); - } - } - } - else - { - Terminal crashTerminal = atTerminal; - if (atTerminal==SpecialTerminals.WholeRow) - crashTerminal = actionTable.getTerminalOfFirstShiftAction(atState); - throw new ShiftReduceConflict((Shift) action, reduce, crashTerminal); - } - } - else if (action instanceof Reduce) - { - //try to find out if production or terminal has higher precedence - Reduce reduce2=(Reduce)action; - int prec = 0; - if (precedences != null) - { - prec = precedences.compare(reduce.getProduction(), reduce2.getProduction().getPrecedenceTerminal()); - if (prec == 1) - { - //reduction has higher precedence, hence reduce - actionTable.set(reduce, atState, atTerminal); - } - else if (prec == -1) - { - //reduction2 has higher precedence, hence shift - actionTable.set(reduce2,atState,atTerminal); - }else - throw new ReduceReduceConflict(reduce,(Reduce)action); - }else - throw new ReduceReduceConflict(reduce,(Reduce)action); - - } - else - throw new GeneratorException("Unknown conflict"); - } - else - { - //empty cell, no conflict - actionTable.set(reduce, atState, atTerminal); - } - } + /** Gets the automaton that was used to create the parsing table. */ + public Automaton getAutomaton() { + return automaton; + } - - /** - * Gets the resulting parsing table. - */ - public LRParsingTable getParsingTable() - { - return parsingTable; - } - - /** - * Gets the automaton that was used to create the parsing table. - */ - public Automaton getAutomaton() - { - return automaton; - } - - /** - * Sets the number of threads (only for parallel programs) - * @param n the new number of threads - */ - public static void setNumThreads(int n) { - numThreads = n; - } - + /** + * Sets the number of threads (only for parallel programs) + * + * @param n the new number of threads + */ + public static void setNumThreads(int n) { + numThreads = n; + } } diff --git a/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/generator/NFA.java b/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/generator/NFA.java index 7c4ae9c03..f82648b53 100644 --- a/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/generator/NFA.java +++ b/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/generator/NFA.java @@ -1,271 +1,198 @@ package edu.tum.cup2.generator; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Set; - import edu.tum.cup2.generator.items.Item; import edu.tum.cup2.generator.states.State; import edu.tum.cup2.grammar.Symbol; import edu.tum.cup2.util.It; - +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Objects; +import java.util.Set; /** - * Represents a non-deterministic automaton (in opposite to {@link Automaton}, which identifies edges only by src and - * symbol!) - * + * Represents a non-deterministic automaton (in opposite to {@link Automaton}, which identifies + * edges only by src and symbol!) + * * @author Gero */ -public class NFA> -{ - private final Set states = new HashSet(); - private final Set> edges = new HashSet>(); - private final Set endStates = new HashSet(); - private S startState = null; - - - public NFA(S startState) - { - states.add(startState); - this.startState = startState; - } - - - public Set getStates() - { - return states; - } - - - public Set> getEdges() - { - return edges; - } - - - public S getStartState() - { - return startState; - } - - - public Set getEndStates() - { - return endStates; - } - - - public void addEdge(S src, Symbol symbol, S dst) - { - addEdge(new Edge(src, symbol, dst)); - } - - - public void addEdge(Edge newEdge) - { - edges.add(newEdge); - states.add(newEdge.getSrc()); - states.add(newEdge.getDest()); - } - - - public void addAcceptingEdge(S src, Symbol symbol, S dest) - { - addEdge(new Edge(src, symbol, dest)); - endStates.add(dest); - } - - - /** - * Gets all edges starting at the given state. - * This implementation is not optimized and runs in O(n). - */ - public LinkedList> getEdgesFrom(S state) - { - final LinkedList> ret = new LinkedList>(); - for (Edge edge : edges) - { - if (edge.getSrc().equals(state)) - { - ret.add(edge); - } - } - return ret; - } - - - /** - * Gets the edge arriving in the given state. - * This implementation is not optimized and runs in O(n). - */ - public List> getEdgeTo(S state) - { - final LinkedList> ret = new LinkedList>(); - for (Edge edge : edges) - { - S dest = edge.getDest(); - if (dest != null && dest.equals(state)) - { - ret.add(edge); - } - } - return ret; - } - - - public boolean isEndState(S state) - { - return endStates.contains(state); - } - - - public boolean isAcceptingEdge(Edge edge) - { - return endStates.contains(edge.getDest()); - } - - - /** - * Pattern: [ srcItem.getProduction() ]... | symbol | [ dstItems ]...([ ACCEPT ]) - */ - @Override - public String toString() - { - final StringBuilder sb = new StringBuilder(); - for (Edge edge : edges) - { - final S src = edge.getSrc(); - sb.append(printItems(src.getItems())); - - sb.append(" | " + edge.getSymbol() + " | "); - - final S dest = edge.getDest(); - sb.append(printItems(dest.getItems())); - if (isEndState(dest)) - { - sb.append("[ ACCEPT ]"); - } - sb.append("\n"); - } - return sb.toString(); - } - - - private String printItems(It items) - { - final StringBuilder sb = new StringBuilder(); - for (I item : items) - { - sb.insert(0, "[" + item.getProduction().toString(item.getPosition()) + "]"); - } - return sb.toString(); - } - - - public static final class Edge> - { - private final S src; - private final Symbol symbol; - private final S dest; - - private final Item srcItem; - private final int hashCache; - - - /** - * Edge which leads to another non-accepting state. - */ - public Edge(S src, Symbol symbol, S dest) - { - this.src = src; - this.symbol = symbol; - this.dest = dest; - - this.srcItem = src.getFirstItem(); - - this.hashCache = calcHashCode(); - } - - - public S getSrc() - { - return src; - } - - - public S getDest() - { - return dest; - } - - - public Symbol getSymbol() - { - return symbol; - } - - - public Item getSrcItem() - { - return srcItem; - } - - - private int calcHashCode() - { - final int prime = 31; - int result = 1; - result = prime * result + ((dest == null) ? 0 : dest.hashCode()); - result = prime * result + ((src == null) ? 0 : src.hashCode()); - result = prime * result + ((symbol == null) ? 0 : symbol.hashCode()); - return result; - } - - - @Override - public int hashCode() - { - return hashCache; - } - - - @Override - public boolean equals(Object obj) - { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - final Edge other = (Edge) obj; - if (dest == null) - { - if (other.dest != null) - return false; - } else if (!dest.equals(other.dest)) - return false; - if (src == null) - { - if (other.src != null) - return false; - } else if (!src.equals(other.src)) - return false; - if (symbol == null) - { - if (other.symbol != null) - return false; - } else if (!symbol.equals(other.symbol)) - return false; - return true; - } - - - @Override - public String toString() - { - return "<" + srcItem + "| " + symbol + ">"; - } - } +public class NFA> { + private final Set states = new HashSet(); + private final Set> edges = new HashSet>(); + private final Set endStates = new HashSet(); + private S startState = null; + + public NFA(S startState) { + states.add(startState); + this.startState = startState; + } + + public Set getStates() { + return states; + } + + public Set> getEdges() { + return edges; + } + + public S getStartState() { + return startState; + } + + public Set getEndStates() { + return endStates; + } + + public void addEdge(S src, Symbol symbol, S dst) { + addEdge(new Edge(src, symbol, dst)); + } + + public void addEdge(Edge newEdge) { + edges.add(newEdge); + states.add(newEdge.getSrc()); + states.add(newEdge.getDest()); + } + + public void addAcceptingEdge(S src, Symbol symbol, S dest) { + addEdge(new Edge(src, symbol, dest)); + endStates.add(dest); + } + + /** + * Gets all edges starting at the given state. This implementation is not optimized and runs in + * O(n). + */ + public LinkedList> getEdgesFrom(S state) { + final LinkedList> ret = new LinkedList>(); + for (Edge edge : edges) { + if (Objects.equals(edge.getSrc(), state)) { + ret.add(edge); + } + } + return ret; + } + + /** + * Gets the edge arriving in the given state. This implementation is not optimized and runs in + * O(n). + */ + public List> getEdgeTo(S state) { + final LinkedList> ret = new LinkedList>(); + for (Edge edge : edges) { + S dest = edge.getDest(); + if (dest != null && Objects.equals(dest, state)) { + ret.add(edge); + } + } + return ret; + } + + public boolean isEndState(S state) { + return endStates.contains(state); + } + + public boolean isAcceptingEdge(Edge edge) { + return endStates.contains(edge.getDest()); + } + + /** Pattern: [ srcItem.getProduction() ]... | symbol | [ dstItems ]...([ ACCEPT ]) */ + @Override + public String toString() { + final StringBuilder sb = new StringBuilder(); + for (Edge edge : edges) { + final S src = edge.getSrc(); + sb.append(printItems(src.getItems())); + + sb.append(" | " + edge.getSymbol() + " | "); + + final S dest = edge.getDest(); + sb.append(printItems(dest.getItems())); + if (isEndState(dest)) { + sb.append("[ ACCEPT ]"); + } + sb.append("\n"); + } + return sb.toString(); + } + + private String printItems(It items) { + final StringBuilder sb = new StringBuilder(); + for (I item : items) { + sb.insert(0, "[" + item.getProduction().toString(item.getPosition()) + "]"); + } + return sb.toString(); + } + + public static final class Edge> { + private final S src; + private final Symbol symbol; + private final S dest; + + private final Item srcItem; + private final int hashCache; + + /** Edge which leads to another non-accepting state. */ + public Edge(S src, Symbol symbol, S dest) { + this.src = src; + this.symbol = symbol; + this.dest = dest; + + this.srcItem = src.getFirstItem(); + + this.hashCache = calcHashCode(); + } + + public S getSrc() { + return src; + } + + public S getDest() { + return dest; + } + + public Symbol getSymbol() { + return symbol; + } + + public Item getSrcItem() { + return srcItem; + } + + private int calcHashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((dest == null) ? 0 : dest.hashCode()); + result = prime * result + ((src == null) ? 0 : src.hashCode()); + result = prime * result + ((symbol == null) ? 0 : symbol.hashCode()); + return result; + } + + @Override + public int hashCode() { + return hashCache; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + final Edge other = (Edge) obj; + if (dest == null) { + if (other.dest != null) return false; + } else if (!Objects.equals(dest, other.dest)) return false; + if (src == null) { + if (other.src != null) return false; + } else if (!Objects.equals(src, other.src)) return false; + if (symbol == null) { + if (other.symbol != null) return false; + } else if (!Objects.equals(symbol, other.symbol)) return false; + return true; + } + + @Override + public String toString() { + return "<" + srcItem + "| " + symbol + ">"; + } + } } diff --git a/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/generator/items/LLkItem.java b/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/generator/items/LLkItem.java index d889cd475..2098dade9 100644 --- a/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/generator/items/LLkItem.java +++ b/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/generator/items/LLkItem.java @@ -1,8 +1,5 @@ package edu.tum.cup2.generator.items; -import java.io.Serializable; -import java.util.Collection; - import edu.tum.cup2.generator.terminals.ITerminalSeq; import edu.tum.cup2.generator.terminals.ITerminalSeqSet; import edu.tum.cup2.generator.terminals.TerminalSeqSet; @@ -10,156 +7,124 @@ import edu.tum.cup2.grammar.SpecialNonTerminals; import edu.tum.cup2.grammar.SpecialTerminals; import edu.tum.cup2.grammar.Symbol; - +import java.io.Serializable; +import java.util.Collection; +import java.util.Objects; /** * The {@link Item}-implementation of an LL(k)-item cellar automaton. - * + * * @author Gero */ -public class LLkItem implements Item, Serializable -{ - private static final long serialVersionUID = 7301061753367113925L; - - public static final LLkItem FAKE_ITEM = new LLkItem(new LR0Item(new Production(-1, SpecialNonTerminals.StartLHS, - SpecialTerminals.EndOfInputStream), 0), ITerminalSeqSet.EMPTY); - - protected final LR0Item kernel; - - protected final ITerminalSeqSet lookahead; - - /** Cached for performance reasons */ - private final int hashCode; - - - /** - * @param production - * @param position - * @param lookaheads - */ - public LLkItem(Production production, int position, ITerminalSeqSet lookaheads) - { - this(new LR0Item(production, position), lookaheads); - } - - - /** - * @param production - * @param position - * @param lookaheads - */ - public LLkItem(Production production, int position, ITerminalSeq... lookaheads) - { - this(new LR0Item(production, position), new TerminalSeqSet(lookaheads)); - } - - - /** - * @param production - * @param position - * @param lookaheads - */ - public LLkItem(Production production, int position, Collection lookaheads) - { - this(new LR0Item(production, position), new TerminalSeqSet(lookaheads)); - } - - - public LLkItem(LR0Item kernel, ITerminalSeqSet lookahead) - { - super(); - this.kernel = kernel; - this.hashCode = calcHashCode(); - this.lookahead = lookahead; - } - - - public ITerminalSeqSet getLookaheads() - { - return lookahead; - } - - - public Production getProduction() - { - return kernel.getProduction(); - } - - - public int getPosition() - { - return kernel.getPosition(); - } - - - public Symbol getNextSymbol() - { - return kernel.getNextSymbol(); - } - - - public boolean isShiftable() - { - return kernel.isShiftable(); - } - - - public boolean isComplete() - { - return !kernel.isShiftable(); - } - - - public LLkItem shift() - { - if (!isShiftable()) - { - throw new RuntimeException("Shifting not possible: Item already closed: " + this); - } - - return new LLkItem(getProduction(), getPosition() + 1, getLookaheads()); - } - - - @Override - public String toString() - { - return "[ LLkItem: " + kernel.toString() + ", Lookahead: " + lookahead.toString() + " ]"; - } - - - private int calcHashCode() - { - final int prime = 31; - int result = 1; - result = prime * result + ((kernel == null) ? 0 : kernel.hashCode()); - return result; - } - - - @Override - public int hashCode() - { - return hashCode; - } - - - @Override - public boolean equals(Object obj) - { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - LLkItem other = (LLkItem) obj; - if (kernel == null) - { - if (other.kernel != null) - return false; - } else if (!kernel.equals(other.kernel)) - return false; - return true; - } +public class LLkItem implements Item, Serializable { + private static final long serialVersionUID = 7301061753367113925L; + + public static final LLkItem FAKE_ITEM = + new LLkItem( + new LR0Item( + new Production(-1, SpecialNonTerminals.StartLHS, SpecialTerminals.EndOfInputStream), + 0), + ITerminalSeqSet.EMPTY); + + protected final LR0Item kernel; + + protected final ITerminalSeqSet lookahead; + + /** Cached for performance reasons */ + private final int hashCode; + + /** + * @param production + * @param position + * @param lookaheads + */ + public LLkItem(Production production, int position, ITerminalSeqSet lookaheads) { + this(new LR0Item(production, position), lookaheads); + } + + /** + * @param production + * @param position + * @param lookaheads + */ + public LLkItem(Production production, int position, ITerminalSeq... lookaheads) { + this(new LR0Item(production, position), new TerminalSeqSet(lookaheads)); + } + + /** + * @param production + * @param position + * @param lookaheads + */ + public LLkItem(Production production, int position, Collection lookaheads) { + this(new LR0Item(production, position), new TerminalSeqSet(lookaheads)); + } + + public LLkItem(LR0Item kernel, ITerminalSeqSet lookahead) { + super(); + this.kernel = kernel; + this.hashCode = calcHashCode(); + this.lookahead = lookahead; + } + + public ITerminalSeqSet getLookaheads() { + return lookahead; + } + + public Production getProduction() { + return kernel.getProduction(); + } + + public int getPosition() { + return kernel.getPosition(); + } + + public Symbol getNextSymbol() { + return kernel.getNextSymbol(); + } + + public boolean isShiftable() { + return kernel.isShiftable(); + } + + public boolean isComplete() { + return !kernel.isShiftable(); + } + + public LLkItem shift() { + if (!isShiftable()) { + throw new RuntimeException("Shifting not possible: Item already closed: " + this); + } + + return new LLkItem(getProduction(), getPosition() + 1, getLookaheads()); + } + + @Override + public String toString() { + return "[ LLkItem: " + kernel.toString() + ", Lookahead: " + lookahead.toString() + " ]"; + } + + private int calcHashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((kernel == null) ? 0 : kernel.hashCode()); + return result; + } + + @Override + public int hashCode() { + return hashCode; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + LLkItem other = (LLkItem) obj; + if (kernel == null) { + if (other.kernel != null) return false; + } else if (!Objects.equals(kernel, other.kernel)) return false; + return true; + } } diff --git a/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/generator/items/LR0Item.java b/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/generator/items/LR0Item.java index 01164191a..80264622a 100644 --- a/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/generator/items/LR0Item.java +++ b/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/generator/items/LR0Item.java @@ -1,155 +1,113 @@ package edu.tum.cup2.generator.items; -import java.util.List; - import edu.tum.cup2.grammar.Production; import edu.tum.cup2.grammar.SpecialTerminals; import edu.tum.cup2.grammar.Symbol; - +import java.util.List; +import java.util.Objects; /** - * An LR(0)-Item is a {@link Production} combined - * with a position in its right hand side. - * - * In many compiler books, this position is visualized - * as a dot within the right hand side, e.g. for the - * production "E → (B)" with the dot at position 1: - * "E → (.B)". - * + * An LR(0)-Item is a {@link Production} combined with a position in its right hand side. + * + *

In many compiler books, this position is visualized as a dot within the right hand side, e.g. + * for the production "E → (B)" with the dot at position 1: "E → (.B)". + * * @author Andreas Wenger */ -public class LR0Item implements Item -{ - - protected final Production production; - protected final int position; - - // cache - protected final Symbol nextSymbol; - protected final int hashCode; - private final boolean shiftable; - - - /** - * Creates a new {@link LR0Item}, using the given {@link Production} and - * position within this production. - * If the position is before an epsilon, it is automatically shifted - * on to the first non-epsilon symbol (or end). - */ - public LR0Item(Production production, int position) - { - this.production = production; - // correct position if epsilons are following - List rhs = production.getRHS(); - int correctedPosition = position; - if (position == 0 && rhs.get(0) == SpecialTerminals.Epsilon) - { - correctedPosition = 1; // immediately shift over epsilon - } - this.position = correctedPosition; - // shiftable - this.shiftable = (this.position < rhs.size()); - // next symbol - if (this.shiftable) - this.nextSymbol = rhs.get(this.position); - else - this.nextSymbol = null; - // hash code - this.hashCode = production.hashCode() + this.position; - } - - - public Production getProduction() - { - return production; - } - - - public int getPosition() - { - return position; - } - - - /** - * Gets the {@link Symbol} right behind the current position, - * or null if there is none left. - */ - public Symbol getNextSymbol() - { - return nextSymbol; - } - - - public boolean isShiftable() - { - return shiftable; - } - - - public boolean isComplete() - { - return !shiftable; - } - - - /** - * Returns this item with the position shifted one symbol further. - */ - public LR0Item shift() - { - if (!isShiftable()) - throw new RuntimeException("Shifting not possible: Item already closed: " + this); - - // return shifted item - return new LR0Item(production, position + 1); - } - - - /** - * @return A new {@link LR0Item} with the same production but shifted until it is complete - */ - public LR0Item complete() - { - if (isComplete()) - { - return this; - } else - { - return new LR0Item(production, production.getRHS().size()); - } - } - - - @Override - public String toString() - { - return production.toString(position); - } - - - @Override - public boolean equals(Object obj) - { - if (obj instanceof LR0Item) - { - LR0Item l = (LR0Item) obj; - // if (production != l.production) //OBSOLETE: - if (!production.equals(l.production)) - return false; - if (position != l.position) - return false; - return true; - } - return false; - } - - - @Override - public int hashCode() - { - return hashCode; - } - - +public class LR0Item implements Item { + + protected final Production production; + protected final int position; + + // cache + protected final Symbol nextSymbol; + protected final int hashCode; + private final boolean shiftable; + + /** + * Creates a new {@link LR0Item}, using the given {@link Production} and position within this + * production. If the position is before an epsilon, it is automatically shifted on to the first + * non-epsilon symbol (or end). + */ + public LR0Item(Production production, int position) { + this.production = production; + // correct position if epsilons are following + List rhs = production.getRHS(); + int correctedPosition = position; + if (position == 0 && rhs.get(0) == SpecialTerminals.Epsilon) { + correctedPosition = 1; // immediately shift over epsilon + } + this.position = correctedPosition; + // shiftable + this.shiftable = (this.position < rhs.size()); + // next symbol + if (this.shiftable) this.nextSymbol = rhs.get(this.position); + else this.nextSymbol = null; + // hash code + this.hashCode = production.hashCode() + this.position; + } + + public Production getProduction() { + return production; + } + + public int getPosition() { + return position; + } + + /** + * Gets the {@link Symbol} right behind the current position, or null if there is + * none left. + */ + public Symbol getNextSymbol() { + return nextSymbol; + } + + public boolean isShiftable() { + return shiftable; + } + + public boolean isComplete() { + return !shiftable; + } + + /** Returns this item with the position shifted one symbol further. */ + public LR0Item shift() { + if (!isShiftable()) + throw new RuntimeException("Shifting not possible: Item already closed: " + this); + + // return shifted item + return new LR0Item(production, position + 1); + } + + /** @return A new {@link LR0Item} with the same production but shifted until it is complete */ + public LR0Item complete() { + if (isComplete()) { + return this; + } else { + return new LR0Item(production, production.getRHS().size()); + } + } + + @Override + public String toString() { + return production.toString(position); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof LR0Item) { + LR0Item l = (LR0Item) obj; + // if (production != l.production) //OBSOLETE: + if (!Objects.equals(production, l.production)) return false; + if (position != l.position) return false; + return true; + } + return false; + } + + @Override + public int hashCode() { + return hashCode; + } } diff --git a/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/generator/items/LR1Item.java b/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/generator/items/LR1Item.java index 209ec93c8..22c0b402e 100644 --- a/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/generator/items/LR1Item.java +++ b/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/generator/items/LR1Item.java @@ -1,223 +1,172 @@ package edu.tum.cup2.generator.items; import static edu.tum.cup2.grammar.SpecialTerminals.Epsilon; + import edu.tum.cup2.generator.FirstSets; import edu.tum.cup2.generator.NullableSet; import edu.tum.cup2.generator.terminals.TerminalSet; import edu.tum.cup2.grammar.Production; import edu.tum.cup2.grammar.Symbol; - +import java.util.Objects; /** - * An LR(1)-Item is an LR(0)-Item, combined - * with a set of lookahead terminals. - * + * An LR(1)-Item is an LR(0)-Item, combined with a set of lookahead terminals. + * * @author Andreas Wenger */ -public final class LR1Item - implements Item -{ - - private final LR0Item kernel; - private final TerminalSet lookaheads; - - //cache - private TerminalSet nextLookaheads = null; //compute on demand - private final int hashCode; - - - /** - * Creates a new {@link LR1Item}, using the given {@link Production}, - * position within this production and set of possible lookahead terminals. - */ - public LR1Item(Production production, int position, TerminalSet lookaheads) - { - this.kernel = new LR0Item(production, position); - this.lookaheads = lookaheads; - //compute hashcode - this.hashCode = this.kernel.hashCode() + lookaheads.hashCode(); - } - - - /** - * Creates a new {@link LR1Item}, using the given {@link LR0Item} - * and set of possible lookahead terminals. - */ - public LR1Item(LR0Item kernel, TerminalSet lookaheads) - { - this.kernel = kernel; - this.lookaheads = lookaheads; - //compute hashcode - this.hashCode = this.kernel.hashCode() + lookaheads.hashCode(); - } - - - public Production getProduction() - { - return kernel.getProduction(); - } - - - public int getPosition() - { - return kernel.getPosition(); - } - - - /** - * Gets the {@link Symbol} right behind the current position, - * or null if there is none left. - */ - public Symbol getNextSymbol() - { - return kernel.getNextSymbol(); - } - - - public boolean isShiftable() - { - return kernel.isShiftable(); - } - - - public boolean isComplete() - { - return kernel.isComplete(); - } - - - /** - * Returns a new {@link LR1Item}, having the same LR(0) kernel as this - * one and containing the union of the lookahead symbols of this item - * and the given ones. If no new lookaheads are given, the original - * LR1Item (this) is returned. - */ - public LR1Item createUnion(TerminalSet lookaheads) - { - if (this.lookaheads.equals(lookaheads)) - { - return this; - } - else - { - return new LR1Item(kernel.production, kernel.position, - this.lookaheads.plusAll(lookaheads)); - } - } - - - /** - * Returns this item with the position shifted one symbol further. - */ - public LR1Item shift() - { - if (kernel.position >= kernel.getProduction().getRHS().size()) - throw new RuntimeException( - "Shifting not possible: Item already closed: " + kernel.production.toString(kernel.position)); - return new LR1Item(kernel.production, kernel.position + 1, lookaheads); - } - - - /** - * Gets the lookahead terminals assigned to this item. - */ - public TerminalSet getLookaheads() - { - return lookaheads; - } - - - /** - * Gets the FIRST set of βa (where this item has the following form: - * "A → α.Xβ with lookahead a" with X being the next symbol), using - * the given precomputed nullable set and FIRST sets for all symbols. - */ - public TerminalSet getNextLookaheads(FirstSets firstSets, NullableSet nullableSet) - { - if (nextLookaheads == null) - { - nextLookaheads = computeNextLookaheadSymbols(firstSets, nullableSet); - } - return nextLookaheads; - } - - - /** - * Gets the FIRST set of βa (where this item has the following form: - * "A → α.Xβ with lookahead a" with X being the next symbol). See - * getNextLookaheads. - */ - TerminalSet computeNextLookaheadSymbols(FirstSets firstSets, NullableSet nullableSet) - { - TerminalSet ret = lookaheads.empty(); - //while the symbols of β (if any) are nullable, collect their FIRST sets. - //when not nullable, collect the symbols and stop. - for (int i = kernel.position + 1; i < kernel.production.getRHS().size(); i++) - { - Symbol symbol = kernel.production.getRHS().get(i); - ret = ret.plusAll(firstSets.get(symbol)); - if (!(symbol == Epsilon || nullableSet.contains(symbol))) //TODO: why "symbol == Epsilon"? redundant to nullableSet - return ret; - } - //since we still did not return, all symbols of β (if any) were nullable, - //so we add all our lookaheads to the result - ret = ret.plusAll(lookaheads); - return ret; - } - - - /** - * Merges this item with the given one and returns the result. - */ - public LR1Item merge(LR1Item item) - { - if (!kernel.equals(item.kernel)) - throw new IllegalArgumentException("Only items with equal LR(0) kernel can be merged!"); - return new LR1Item(kernel.production, kernel.position, lookaheads.plusAll(item.lookaheads)); - } - - - @Override public String toString() - { - StringBuilder s = new StringBuilder(kernel.production.toString(kernel.position)); - s.append(" ["); - s.append(lookaheads.toString()); - s.append("]"); - return s.toString(); - } - - - @Override public boolean equals(Object obj) - { - if (obj instanceof LR1Item) - { - LR1Item l = (LR1Item) obj; - if (!kernel.equals(l.kernel)) - return false; - if (!lookaheads.equals(l.lookaheads)) - return false; - return true; - } - return false; - } - - - public boolean equalsLR0(LR1Item item) - { - return kernel.equals(item.kernel); - } - - - @Override public int hashCode() - { - return hashCode; - } - - - public LR0Item getLR0Kernel() - { - return kernel; - } - +public final class LR1Item implements Item { + + private final LR0Item kernel; + private final TerminalSet lookaheads; + + // cache + private TerminalSet nextLookaheads = null; // compute on demand + private final int hashCode; + + /** + * Creates a new {@link LR1Item}, using the given {@link Production}, position within this + * production and set of possible lookahead terminals. + */ + public LR1Item(Production production, int position, TerminalSet lookaheads) { + this.kernel = new LR0Item(production, position); + this.lookaheads = lookaheads; + // compute hashcode + this.hashCode = this.kernel.hashCode() + lookaheads.hashCode(); + } + + /** + * Creates a new {@link LR1Item}, using the given {@link LR0Item} and set of possible lookahead + * terminals. + */ + public LR1Item(LR0Item kernel, TerminalSet lookaheads) { + this.kernel = kernel; + this.lookaheads = lookaheads; + // compute hashcode + this.hashCode = this.kernel.hashCode() + lookaheads.hashCode(); + } + + public Production getProduction() { + return kernel.getProduction(); + } + + public int getPosition() { + return kernel.getPosition(); + } + + /** + * Gets the {@link Symbol} right behind the current position, or null if there is + * none left. + */ + public Symbol getNextSymbol() { + return kernel.getNextSymbol(); + } + + public boolean isShiftable() { + return kernel.isShiftable(); + } + + public boolean isComplete() { + return kernel.isComplete(); + } + + /** + * Returns a new {@link LR1Item}, having the same LR(0) kernel as this one and containing the + * union of the lookahead symbols of this item and the given ones. If no new lookaheads are given, + * the original LR1Item (this) is returned. + */ + public LR1Item createUnion(TerminalSet lookaheads) { + if (Objects.equals(this.lookaheads, lookaheads)) { + return this; + } else { + return new LR1Item(kernel.production, kernel.position, this.lookaheads.plusAll(lookaheads)); + } + } + + /** Returns this item with the position shifted one symbol further. */ + public LR1Item shift() { + if (kernel.position >= kernel.getProduction().getRHS().size()) + throw new RuntimeException( + "Shifting not possible: Item already closed: " + + kernel.production.toString(kernel.position)); + return new LR1Item(kernel.production, kernel.position + 1, lookaheads); + } + + /** Gets the lookahead terminals assigned to this item. */ + public TerminalSet getLookaheads() { + return lookaheads; + } + + /** + * Gets the FIRST set of βa (where this item has the following form: "A → α.Xβ with lookahead a" + * with X being the next symbol), using the given precomputed nullable set and FIRST sets for all + * symbols. + */ + public TerminalSet getNextLookaheads(FirstSets firstSets, NullableSet nullableSet) { + if (nextLookaheads == null) { + nextLookaheads = computeNextLookaheadSymbols(firstSets, nullableSet); + } + return nextLookaheads; + } + + /** + * Gets the FIRST set of βa (where this item has the following form: "A → α.Xβ with lookahead a" + * with X being the next symbol). See getNextLookaheads. + */ + TerminalSet computeNextLookaheadSymbols(FirstSets firstSets, NullableSet nullableSet) { + TerminalSet ret = lookaheads.empty(); + // while the symbols of β (if any) are nullable, collect their FIRST sets. + // when not nullable, collect the symbols and stop. + for (int i = kernel.position + 1; i < kernel.production.getRHS().size(); i++) { + Symbol symbol = kernel.production.getRHS().get(i); + ret = ret.plusAll(firstSets.get(symbol)); + if (!(symbol == Epsilon + || nullableSet.contains( + symbol))) // TODO: why "symbol == Epsilon"? redundant to nullableSet + return ret; + } + // since we still did not return, all symbols of β (if any) were nullable, + // so we add all our lookaheads to the result + ret = ret.plusAll(lookaheads); + return ret; + } + + /** Merges this item with the given one and returns the result. */ + public LR1Item merge(LR1Item item) { + if (!Objects.equals(kernel, item.kernel)) + throw new IllegalArgumentException("Only items with equal LR(0) kernel can be merged!"); + return new LR1Item(kernel.production, kernel.position, lookaheads.plusAll(item.lookaheads)); + } + + @Override + public String toString() { + StringBuilder s = new StringBuilder(kernel.production.toString(kernel.position)); + s.append(" ["); + s.append(lookaheads.toString()); + s.append("]"); + return s.toString(); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof LR1Item) { + LR1Item l = (LR1Item) obj; + if (!Objects.equals(kernel, l.kernel)) return false; + if (!Objects.equals(lookaheads, l.lookaheads)) return false; + return true; + } + return false; + } + + public boolean equalsLR0(LR1Item item) { + return Objects.equals(kernel, item.kernel); + } + + @Override + public int hashCode() { + return hashCode; + } + public LR0Item getLR0Kernel() { + return kernel; + } } diff --git a/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/generator/states/LALR1CPState.java b/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/generator/states/LALR1CPState.java index f1927cd27..c67652896 100644 --- a/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/generator/states/LALR1CPState.java +++ b/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/generator/states/LALR1CPState.java @@ -5,12 +5,6 @@ import static edu.tum.cup2.util.CollectionTools.set; import static edu.tum.cup2.util.Tuple2.t; -import java.util.Collection; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Set; - import edu.tum.cup2.generator.FirstSets; import edu.tum.cup2.generator.GrammarInfo; import edu.tum.cup2.generator.NullableSet; @@ -22,176 +16,147 @@ import edu.tum.cup2.grammar.Production; import edu.tum.cup2.grammar.Symbol; import edu.tum.cup2.util.Tuple2; - +import java.util.Collection; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; /** - * LALR(1) state, consisting of a list of LALR(1)-CP items - * (LR(0) items with some lookaheads and with context propagation - * links). - * + * LALR(1) state, consisting of a list of LALR(1)-CP items (LR(0) items with some lookaheads and + * with context propagation links). + * * @author Andreas Wenger */ -public final class LALR1CPState - extends LRState -{ - - //cache - private final int hashCode; - private final Map stripped2Full = map(); - - - /** - * Creates a new {@link LALR1CPState} with the given items (and their kernels, - * needed for performance reasons). - */ - //TODO: Parameter strippedItems is redundant - public LALR1CPState(Collection items) - { - super(items); - //compute hashcode - int sum = 0; - for (LALR1CPItem it: items){ - LR0Item item = it.getLR0Item(); - sum+=item.hashCode(); - stripped2Full.put(item,it); - } - this.hashCode = sum; - } - - - /** - * Returns the closure of this {@link LALR1CPState} as another - * {@link LALR1CPState}. - */ - @Override public LALR1CPState closure(GrammarInfo grammarInfo) - { - //the closure contains all items of the source... (here indexed by LR(0) kernel) - Map retItems = map(); - for (LALR1CPItem item : this.items) - retItems.put(item.getLR0Item(), item); - //... and the following ones: for any item "A → α.Xβ with lookahead z", - //any "X → γ" and any w ∈ FIRST(β), add "X → .γ with lookahead w". if "ɛ ∈ w" is - //nullable, add a context propagation link from the source item to the target item. - LinkedList queue = new LinkedList(); - queue.addAll(retItems.keySet()); - FirstSets firstSets = grammarInfo.getFirstSets(); - NullableSet nullableSet = grammarInfo.getNullableSet(); - while (!queue.isEmpty()) - { - LR0Item sourceKernel = queue.removeFirst(); //itemKernel is "A → α.Xβ" - LALR1CPItem source = retItems.get(sourceKernel); //item is "A → α.Xβ with lookahead z and some CP links" - Symbol nextSymbol = source.getNextSymbol(); - if (nextSymbol instanceof NonTerminal) //nextSymbol is "X" - { - Tuple2 next = source.getNextLookaheadsAndCP(firstSets, nullableSet); - EfficientTerminalSet firstSet = next.get1(); //all "w"s - Boolean needCP = next.get2(); //true if CP link is needed - for (Production p : grammarInfo.getProductionsFrom((NonTerminal) nextSymbol)) //p is each "X → γ" - { - LR0Item destKernel = new LR0Item(p, 0); - //look, if there is already a LALR(1)-CP item with that LR(0) kernel. if so, add the - //new lookahead symbols there. If not, add the LALR(1)-CP item to the result set. - LALR1CPItem sameKernelItem = retItems.get(destKernel); - if (sameKernelItem != null) - { - //add lookaheads to existing LALR1CPItem - retItems.put(destKernel, sameKernelItem.plusLookaheads(firstSet)); - } - else - { - //new item - retItems.put(destKernel, new LALR1CPItem(new LR0Item(p, 0), firstSet)); - queue.add(destKernel); - } - //if required, add CP link to source item - if (needCP) - { - source = source.plusClosureCPLink(destKernel); - retItems.put(sourceKernel, source); - } - } - } - } - return new LALR1CPState(retItems.values()); - } - - - /** - * Use {@link #goToCP(Symbol)} instead. - */ - @Deprecated @Override public LALR1CPState goTo(Symbol symbol) - { - throw new RuntimeException(); - } - - - /** - * Moves the position of the items one step further when - * the given symbol follows and returns them as a new state - * (only the kernel, without closure). - * As a second return value, the go-to CP links are returned, - * but the target state is still null (because we do not now - * yet, if the target state is not already existing). - * For a description of this algorithm, see Appel's book, page 60. - */ - public Tuple2> goToCP(Symbol symbol) - { - Set targetItems = set(); - List cpLinks = llist(); - //find all items where the given symbol follows and add them shifted - for (LALR1CPItem source : items) - { - if (source.getNextSymbol() == symbol) - { - LALR1CPItem target = source.shift(); - targetItems.add(target); - //leave the target state null for now, will be set later - cpLinks.add(new CPGoToLink(source, null, target.getLR0Item())); - } - } - LALR1CPState targetState = new LALR1CPState(targetItems); - return t(targetState, cpLinks); - } - - - /** - * Gets the LALR1CPItem which belongs to the given LR(0) kernel. - */ - public LALR1CPItem getItemWithLookaheadByLR0Item(LR0Item stripped) - { - LALR1CPItem ret = stripped2Full.get(stripped); - if (ret==null) throw new IllegalArgumentException("Unknown kernel " + stripped); - return ret; - } - - - public Set getStrippedItems() - { - return stripped2Full.keySet(); - } - - - /** - * Returns true, if the given object is a {@link LALR1CPState} and if - * it has exactly the same LR(0) items. - */ - @Override public boolean equals(Object obj) - { - if (obj instanceof LALR1CPState) - { - LALR1CPState s = (LALR1CPState) obj; - return s.stripped2Full.keySet().equals(stripped2Full.keySet()); - } - return false; - } - - - @Override public int hashCode() - { - return hashCode; - } - - - - +public final class LALR1CPState extends LRState { + + // cache + private final int hashCode; + private final Map stripped2Full = map(); + + /** + * Creates a new {@link LALR1CPState} with the given items (and their kernels, needed for + * performance reasons). + */ + // TODO: Parameter strippedItems is redundant + public LALR1CPState(Collection items) { + super(items); + // compute hashcode + int sum = 0; + for (LALR1CPItem it : items) { + LR0Item item = it.getLR0Item(); + sum += item.hashCode(); + stripped2Full.put(item, it); + } + this.hashCode = sum; + } + + /** Returns the closure of this {@link LALR1CPState} as another {@link LALR1CPState}. */ + @Override + public LALR1CPState closure(GrammarInfo grammarInfo) { + // the closure contains all items of the source... (here indexed by LR(0) kernel) + Map retItems = map(); + for (LALR1CPItem item : this.items) retItems.put(item.getLR0Item(), item); + // ... and the following ones: for any item "A → α.Xβ with lookahead z", + // any "X → γ" and any w ∈ FIRST(β), add "X → .γ with lookahead w". if "ɛ ∈ w" is + // nullable, add a context propagation link from the source item to the target item. + LinkedList queue = new LinkedList(); + queue.addAll(retItems.keySet()); + FirstSets firstSets = grammarInfo.getFirstSets(); + NullableSet nullableSet = grammarInfo.getNullableSet(); + while (!queue.isEmpty()) { + LR0Item sourceKernel = queue.removeFirst(); // itemKernel is "A → α.Xβ" + LALR1CPItem source = + retItems.get(sourceKernel); // item is "A → α.Xβ with lookahead z and some CP links" + Symbol nextSymbol = source.getNextSymbol(); + if (nextSymbol instanceof NonTerminal) // nextSymbol is "X" + { + Tuple2 next = + source.getNextLookaheadsAndCP(firstSets, nullableSet); + EfficientTerminalSet firstSet = next.get1(); // all "w"s + Boolean needCP = next.get2(); // true if CP link is needed + for (Production p : + grammarInfo.getProductionsFrom((NonTerminal) nextSymbol)) // p is each "X → γ" + { + LR0Item destKernel = new LR0Item(p, 0); + // look, if there is already a LALR(1)-CP item with that LR(0) kernel. if so, add the + // new lookahead symbols there. If not, add the LALR(1)-CP item to the result set. + LALR1CPItem sameKernelItem = retItems.get(destKernel); + if (sameKernelItem != null) { + // add lookaheads to existing LALR1CPItem + retItems.put(destKernel, sameKernelItem.plusLookaheads(firstSet)); + } else { + // new item + retItems.put(destKernel, new LALR1CPItem(new LR0Item(p, 0), firstSet)); + queue.add(destKernel); + } + // if required, add CP link to source item + if (needCP) { + source = source.plusClosureCPLink(destKernel); + retItems.put(sourceKernel, source); + } + } + } + } + return new LALR1CPState(retItems.values()); + } + + /** Use {@link #goToCP(Symbol)} instead. */ + @Deprecated + @Override + public LALR1CPState goTo(Symbol symbol) { + throw new RuntimeException(); + } + + /** + * Moves the position of the items one step further when the given symbol follows and returns them + * as a new state (only the kernel, without closure). As a second return value, the go-to CP links + * are returned, but the target state is still null (because we do not now yet, if the target + * state is not already existing). For a description of this algorithm, see Appel's book, page 60. + */ + public Tuple2> goToCP(Symbol symbol) { + Set targetItems = set(); + List cpLinks = llist(); + // find all items where the given symbol follows and add them shifted + for (LALR1CPItem source : items) { + if (source.getNextSymbol() == symbol) { + LALR1CPItem target = source.shift(); + targetItems.add(target); + // leave the target state null for now, will be set later + cpLinks.add(new CPGoToLink(source, null, target.getLR0Item())); + } + } + LALR1CPState targetState = new LALR1CPState(targetItems); + return t(targetState, cpLinks); + } + + /** Gets the LALR1CPItem which belongs to the given LR(0) kernel. */ + public LALR1CPItem getItemWithLookaheadByLR0Item(LR0Item stripped) { + LALR1CPItem ret = stripped2Full.get(stripped); + if (ret == null) throw new IllegalArgumentException("Unknown kernel " + stripped); + return ret; + } + + public Set getStrippedItems() { + return stripped2Full.keySet(); + } + + /** + * Returns true, if the given object is a {@link LALR1CPState} and if it has exactly the same + * LR(0) items. + */ + @Override + public boolean equals(Object obj) { + if (obj instanceof LALR1CPState) { + LALR1CPState s = (LALR1CPState) obj; + return Objects.equals(s.stripped2Full.keySet(), stripped2Full.keySet()); + } + return false; + } + + @Override + public int hashCode() { + return hashCode; + } } diff --git a/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/generator/states/LLkState.java b/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/generator/states/LLkState.java index ed4adb274..935ba88c5 100644 --- a/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/generator/states/LLkState.java +++ b/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/generator/states/LLkState.java @@ -1,133 +1,102 @@ package edu.tum.cup2.generator.states; +import edu.tum.cup2.generator.items.LLkItem; +import edu.tum.cup2.generator.terminals.ITerminalSeqSet; +import edu.tum.cup2.parser.LLkParser; import java.io.Serializable; import java.util.Arrays; import java.util.Collection; import java.util.Iterator; - -import edu.tum.cup2.generator.items.LLkItem; -import edu.tum.cup2.generator.terminals.ITerminalSeqSet; -import edu.tum.cup2.parser.LLkParser; - +import java.util.Objects; /** - * This class holds the state of a item pushdown automaton for a {@link LLkParser}. LR counterpart: {@link LRState} - * + * This class holds the state of a item pushdown automaton for a {@link LLkParser}. LR counterpart: + * {@link LRState} + * * @author Gero - * */ -public class LLkState extends State implements Serializable -{ - private static final long serialVersionUID = 9051334334450461555L; - - /** The top-most {@link LLkItem} on the parsers stack */ - protected final LLkItem item0; - - /** The second top-most {@link LLkItem} on the parsers stack */ - protected final LLkItem item1; - - /** Cached for performance-reasons */ - private final int hashCode; - - - /** - * @param items - */ - public LLkState(Collection items) - { - super(items); - final Iterator it = items.iterator(); - this.item0 = it.next(); - if (it.hasNext()) - { - this.item1 = it.next(); - } else - { - this.item1 = null; - } - hashCode = calcHashCode(); - } - - - public LLkState(LLkItem item0) - { - super(Arrays.asList(item0)); - this.item0 = item0; - this.item1 = null; - hashCode = calcHashCode(); - } - - - public LLkState(LLkItem item1, LLkItem item0) - { - super(Arrays.asList(item0, item1)); - this.item0 = item0; - this.item1 = item1; - hashCode = calcHashCode(); - } - - - public LLkItem getSecondItem() - { - return this.item1; - } - - - public boolean isTopItemComplete() - { - return this.item0.isComplete(); - } - - - public ITerminalSeqSet getLookahead() - { - return this.item0.getLookaheads(); - } - - - @Override - public String toString() - { - final Iterator it = this.items.iterator(); - String result = "[ LLkState: \n " + it.next().toString(); - while (it.hasNext()) - { - result += " ,\n " + it.next().toString(); - } - result += "\n]"; - return result; - } - - - private int calcHashCode() - { - final int prime = 31; - int result = 1; - for (LLkItem item : this.items) - { - result = prime * result + ((item == null) ? 0 : item.hashCode()); - } - return result; - } - - - @Override - public int hashCode() - { - return hashCode; - } - - - @Override - public boolean equals(Object obj) - { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - LLkState other = (LLkState) obj; - return this.items.equals(other.items); - } +public class LLkState extends State implements Serializable { + private static final long serialVersionUID = 9051334334450461555L; + + /** The top-most {@link LLkItem} on the parsers stack */ + protected final LLkItem item0; + + /** The second top-most {@link LLkItem} on the parsers stack */ + protected final LLkItem item1; + + /** Cached for performance-reasons */ + private final int hashCode; + + /** @param items */ + public LLkState(Collection items) { + super(items); + final Iterator it = items.iterator(); + this.item0 = it.next(); + if (it.hasNext()) { + this.item1 = it.next(); + } else { + this.item1 = null; + } + hashCode = calcHashCode(); + } + + public LLkState(LLkItem item0) { + super(Arrays.asList(item0)); + this.item0 = item0; + this.item1 = null; + hashCode = calcHashCode(); + } + + public LLkState(LLkItem item1, LLkItem item0) { + super(Arrays.asList(item0, item1)); + this.item0 = item0; + this.item1 = item1; + hashCode = calcHashCode(); + } + + public LLkItem getSecondItem() { + return this.item1; + } + + public boolean isTopItemComplete() { + return this.item0.isComplete(); + } + + public ITerminalSeqSet getLookahead() { + return this.item0.getLookaheads(); + } + + @Override + public String toString() { + final Iterator it = this.items.iterator(); + String result = "[ LLkState: \n " + it.next().toString(); + while (it.hasNext()) { + result += " ,\n " + it.next().toString(); + } + result += "\n]"; + return result; + } + + private int calcHashCode() { + final int prime = 31; + int result = 1; + for (LLkItem item : this.items) { + result = prime * result + ((item == null) ? 0 : item.hashCode()); + } + return result; + } + + @Override + public int hashCode() { + return hashCode; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + LLkState other = (LLkState) obj; + return Objects.equals(this.items, other.items); + } } diff --git a/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/generator/states/LR1State.java b/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/generator/states/LR1State.java index 37b056130..edead302d 100644 --- a/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/generator/states/LR1State.java +++ b/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/generator/states/LR1State.java @@ -1,9 +1,5 @@ package edu.tum.cup2.generator.states; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedList; - import edu.tum.cup2.generator.FirstSets; import edu.tum.cup2.generator.GrammarInfo; import edu.tum.cup2.generator.NullableSet; @@ -14,250 +10,203 @@ import edu.tum.cup2.grammar.Production; import edu.tum.cup2.grammar.Symbol; import edu.tum.cup2.grammar.Terminal; - +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.Objects; /** - * LR(1) state (immutable, no side effects possible), consisting of a list of - * LR(1) items. - * - * Methods related to the nucleus were removed (since the author of this - * class doesn't know anything about them). - * + * LR(1) state (immutable, no side effects possible), consisting of a list of LR(1) items. + * + *

Methods related to the nucleus were removed (since the author of this class doesn't know + * anything about them). + * * @author Andreas Wenger */ -public class LR1State - extends LRState -{ - - - private final LR0State kernel; - private final HashMap itemsWithKernels; - private final int hashCode; - - //cache - private LR1State closureCache = null; - - - public LR1State(HashSet items) - { - super(items); - //hash code - int sum = 0; - //create map with LR(0) kernels as key - itemsWithKernels = new HashMap(); - for (LR1Item item : items) - { - itemsWithKernels.put(item.getLR0Kernel(), item); - sum += item.hashCode(); - } - kernel = new LR0State(itemsWithKernels.keySet()); - this.hashCode = sum; - } - - - /** - * Returns the closure of this {@link LR1State} as another {@link LR1State}. - */ - @Override public LR1State closure(GrammarInfo grammarInfo) - { - if (closureCache == null) - closureCache = new LR1State(closureItems(grammarInfo)); - return closureCache; - } - - - /** - * Returns the closure of the {@link LR1Item}s of this state as set of - * {@link LR1Item}s. For a description of this algorithm, see Appel's book, - * page 63. - * - * TODO: optimization possible by using persistent data structures? - */ - private HashSet closureItems(GrammarInfo grammarInfo) - { - //the closure contains all items of the source... - //(we use a map with the LR(0)-part as the key for very fast access, - //since we will probably often add new lookahead symbols to existing LR1Items) - HashMap items = new HashMap(); - for (LR1Item item : this.items) - { - items.put(item.getLR0Kernel(), item.getLookaheads()); - } - // ... and the following ones: for any item "A → α.Xβ with lookahead z", - // any "X → γ" and any w ∈ FIRST(βz), add "X → .γ with lookahead w" - LinkedList queue = new LinkedList(); - queue.addAll(this.items); - FirstSets firstSets = grammarInfo.getFirstSets(); - NullableSet nullableSet = grammarInfo.getNullableSet(); - while (!queue.isEmpty()) - { - LR1Item item = queue.removeFirst(); //item is A → α.Xβ with lookahead z" - Symbol nextSymbol = item.getNextSymbol(); - if (nextSymbol instanceof NonTerminal) //nextSymbol is "X" - { - TerminalSet firstSet = item.getNextLookaheads(firstSets, nullableSet); //all "w"s - for (Production p : grammarInfo.getProductionsFrom((NonTerminal) nextSymbol)) //p is each "X → γ" - { - LR0Item newItemLR0 = new LR0Item(p, 0); - //look, if there is already a LR(1) item with that LR(0) kernel. if so, add the - //new lookahead symbols there. If not, add the LR(1) item to the result set. - TerminalSet sameKernelItemLookaheads = items.get(newItemLR0); - if (sameKernelItemLookaheads != null) - { - //add to existing LR1Item - TerminalSet newLookaheads = sameKernelItemLookaheads.plusAll(firstSet); - //if new lookahead was found, add again to queue - if (!newLookaheads.equals(sameKernelItemLookaheads)) - { - items.put(newItemLR0, newLookaheads); - queue.add(new LR1Item(newItemLR0, newLookaheads)); - } - } - else - { - //new item - items.put(newItemLR0, firstSet); - queue.add(new LR1Item(newItemLR0, firstSet)); - } - } - } - } - //collect resulting LR(1) items - HashSet ret = new HashSet(); - for (LR0Item itemLR0 : items.keySet()) - { - ret.add(new LR1Item(itemLR0.getProduction(), itemLR0.getPosition(), items.get(itemLR0))); - } - return ret; - } - - - /** - * Moves the position of the items one step further when the given symbol - * follows and returns them as a new state (only the kernel, without - * closure). For a description of this algorithm, see Appel's book, page 62. - */ - @Override public LR1State goTo(Symbol symbol) - { - HashSet ret = new HashSet(); - //find all items where the given symbol follows and add them shifted - for (LR1Item item : items) - { - if (item.getNextSymbol() == symbol) - { - ret.add(item.shift()); - } - } - return new LR1State(ret); - } - - - /** - * Gets the number of items (when each item has only one lookahead symbol, - * e.g. an item with 3 lookahead symbols counts as 3 items). - * Useful for debugging and testing. - */ - int getSimpleItemsCount() - { - int ret = 0; - for (LR1Item item : items) - { - ret += item.getLookaheads().getTerminals().size(); - } - return ret; - } - - - /** - * Returns true, if this state contains an item with the given production, - * position and lookahead symbol. Useful for debugging and testing. - */ - boolean containsSimpleItem(Production production, int position, Terminal lookahead) - { - for (LR1Item item : items) - { - if (item.getProduction() == production && item.getPosition() == position - && item.getLookaheads().contains(lookahead)) - return true; - } - return false; - } - - - /** - * Gets the item of this state which has the given LR(0) kernel, - * or null, if there is no such item. - */ - public LR1Item getItemByLR0Kernel(LR0Item kernel) - { - return itemsWithKernels.get(kernel); - } - - - /** - * Merges this state with the given one and returns the result. - * Only works, if both states have equal LR(0) kernels. - */ - public LR1State merge(LR1State state) - { - HashSet items = new HashSet(); - for (LR1Item item1 : this.items) - { - LR1Item newItem = item1; - - //find lr0-equal item and merge them - LR1Item item2 = state.getItemByLR0Kernel(item1.getLR0Kernel()); - newItem = newItem.merge(item2); - - items.add(newItem); - } - return new LR1State(items); - } - - - @Override public boolean equals(Object obj) - { - if (obj instanceof LR1State) - { - LR1State s = (LR1State) obj; - if (items.size() != s.items.size()) - return false; - for (LR1Item item : items) - { - if (!s.items.contains(item)) - return false; - } - return true; - } - return false; - } - - - /** - * Gets the LR(0) kernel of this state. - */ - public LR0State getLR0Kernel() - { - return kernel; - } - - - @Override public int hashCode() - { - return hashCode; - } - - - /** - * Returns true, when the LR(0) kernel of the state - * is equal to the LR(0) kernel of the given state. - */ - public boolean equalsLR0(LR1State state) - { - //first check for same hash code for performance reasons - return kernel.hashCode() == state.kernel.hashCode() && - kernel.equals(state.kernel); - } - - +public class LR1State extends LRState { + + private final LR0State kernel; + private final HashMap itemsWithKernels; + private final int hashCode; + + // cache + private LR1State closureCache = null; + + public LR1State(HashSet items) { + super(items); + // hash code + int sum = 0; + // create map with LR(0) kernels as key + itemsWithKernels = new HashMap(); + for (LR1Item item : items) { + itemsWithKernels.put(item.getLR0Kernel(), item); + sum += item.hashCode(); + } + kernel = new LR0State(itemsWithKernels.keySet()); + this.hashCode = sum; + } + + /** Returns the closure of this {@link LR1State} as another {@link LR1State}. */ + @Override + public LR1State closure(GrammarInfo grammarInfo) { + if (closureCache == null) closureCache = new LR1State(closureItems(grammarInfo)); + return closureCache; + } + + /** + * Returns the closure of the {@link LR1Item}s of this state as set of {@link LR1Item}s. For a + * description of this algorithm, see Appel's book, page 63. + * + *

TODO: optimization possible by using persistent data structures? + */ + private HashSet closureItems(GrammarInfo grammarInfo) { + // the closure contains all items of the source... + // (we use a map with the LR(0)-part as the key for very fast access, + // since we will probably often add new lookahead symbols to existing LR1Items) + HashMap items = new HashMap(); + for (LR1Item item : this.items) { + items.put(item.getLR0Kernel(), item.getLookaheads()); + } + // ... and the following ones: for any item "A → α.Xβ with lookahead z", + // any "X → γ" and any w ∈ FIRST(βz), add "X → .γ with lookahead w" + LinkedList queue = new LinkedList(); + queue.addAll(this.items); + FirstSets firstSets = grammarInfo.getFirstSets(); + NullableSet nullableSet = grammarInfo.getNullableSet(); + while (!queue.isEmpty()) { + LR1Item item = queue.removeFirst(); // item is A → α.Xβ with lookahead z" + Symbol nextSymbol = item.getNextSymbol(); + if (nextSymbol instanceof NonTerminal) // nextSymbol is "X" + { + TerminalSet firstSet = item.getNextLookaheads(firstSets, nullableSet); // all "w"s + for (Production p : + grammarInfo.getProductionsFrom((NonTerminal) nextSymbol)) // p is each "X → γ" + { + LR0Item newItemLR0 = new LR0Item(p, 0); + // look, if there is already a LR(1) item with that LR(0) kernel. if so, add the + // new lookahead symbols there. If not, add the LR(1) item to the result set. + TerminalSet sameKernelItemLookaheads = items.get(newItemLR0); + if (sameKernelItemLookaheads != null) { + // add to existing LR1Item + TerminalSet newLookaheads = sameKernelItemLookaheads.plusAll(firstSet); + // if new lookahead was found, add again to queue + if (!Objects.equals(newLookaheads, sameKernelItemLookaheads)) { + items.put(newItemLR0, newLookaheads); + queue.add(new LR1Item(newItemLR0, newLookaheads)); + } + } else { + // new item + items.put(newItemLR0, firstSet); + queue.add(new LR1Item(newItemLR0, firstSet)); + } + } + } + } + // collect resulting LR(1) items + HashSet ret = new HashSet(); + for (LR0Item itemLR0 : items.keySet()) { + ret.add(new LR1Item(itemLR0.getProduction(), itemLR0.getPosition(), items.get(itemLR0))); + } + return ret; + } + + /** + * Moves the position of the items one step further when the given symbol follows and returns them + * as a new state (only the kernel, without closure). For a description of this algorithm, see + * Appel's book, page 62. + */ + @Override + public LR1State goTo(Symbol symbol) { + HashSet ret = new HashSet(); + // find all items where the given symbol follows and add them shifted + for (LR1Item item : items) { + if (item.getNextSymbol() == symbol) { + ret.add(item.shift()); + } + } + return new LR1State(ret); + } + + /** + * Gets the number of items (when each item has only one lookahead symbol, e.g. an item with 3 + * lookahead symbols counts as 3 items). Useful for debugging and testing. + */ + int getSimpleItemsCount() { + int ret = 0; + for (LR1Item item : items) { + ret += item.getLookaheads().getTerminals().size(); + } + return ret; + } + + /** + * Returns true, if this state contains an item with the given production, position and lookahead + * symbol. Useful for debugging and testing. + */ + boolean containsSimpleItem(Production production, int position, Terminal lookahead) { + for (LR1Item item : items) { + if (item.getProduction() == production + && item.getPosition() == position + && item.getLookaheads().contains(lookahead)) return true; + } + return false; + } + + /** + * Gets the item of this state which has the given LR(0) kernel, or null, if there is + * no such item. + */ + public LR1Item getItemByLR0Kernel(LR0Item kernel) { + return itemsWithKernels.get(kernel); + } + + /** + * Merges this state with the given one and returns the result. Only works, if both states have + * equal LR(0) kernels. + */ + public LR1State merge(LR1State state) { + HashSet items = new HashSet(); + for (LR1Item item1 : this.items) { + LR1Item newItem = item1; + + // find lr0-equal item and merge them + LR1Item item2 = state.getItemByLR0Kernel(item1.getLR0Kernel()); + newItem = newItem.merge(item2); + + items.add(newItem); + } + return new LR1State(items); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof LR1State) { + LR1State s = (LR1State) obj; + if (items.size() != s.items.size()) return false; + for (LR1Item item : items) { + if (!s.items.contains(item)) return false; + } + return true; + } + return false; + } + + /** Gets the LR(0) kernel of this state. */ + public LR0State getLR0Kernel() { + return kernel; + } + + @Override + public int hashCode() { + return hashCode; + } + + /** + * Returns true, when the LR(0) kernel of the state is equal to the LR(0) kernel of the given + * state. + */ + public boolean equalsLR0(LR1State state) { + // first check for same hash code for performance reasons + return kernel.hashCode() == state.kernel.hashCode() && Objects.equals(kernel, state.kernel); + } } diff --git a/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/generator/terminals/TerminalSeq.java b/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/generator/terminals/TerminalSeq.java index dfe5ef776..afd735518 100644 --- a/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/generator/terminals/TerminalSeq.java +++ b/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/generator/terminals/TerminalSeq.java @@ -1,197 +1,144 @@ package edu.tum.cup2.generator.terminals; +import edu.tum.cup2.grammar.Terminal; +import edu.tum.cup2.util.It; import java.util.Collection; import java.util.Iterator; import java.util.LinkedList; - -import edu.tum.cup2.grammar.Terminal; -import edu.tum.cup2.util.It; - +import java.util.Objects; /** * Naive, mutable implementation of {@link ITerminalSeq}. - * + * * @author Gero - * */ -public class TerminalSeq implements ITerminalSeq -{ - private static final long serialVersionUID = -2583663816926783360L; - - protected final LinkedList terminals; - - - public TerminalSeq() - { - this.terminals = new LinkedList(); - } - - - /** - * The sequence is based on the ordering of the collections iterator - * - * @param terminals - */ - public TerminalSeq(Collection terminals) - { - this.terminals = new LinkedList(terminals); - } - - - /** - * The sequence is based on the ordering of the iterator - * - * @param iterator - */ - public TerminalSeq(Iterator iterator) - { - this.terminals = new LinkedList(); - while (iterator.hasNext()) - { - this.terminals.add(iterator.next()); - } - } - - - /** - * @param terminals - */ - public TerminalSeq(Terminal... terminals) - { - this.terminals = new LinkedList(); - for (Terminal terminal : terminals) - { - this.terminals.add(terminal); - } - } - - - public ITerminalSeq concatenate(ITerminalSeq other) - { - for (Terminal terminal : other.getTerminals()) - { - terminals.add(terminal); - } - return this; - } - - - public ITerminalSeq append(Terminal terminal) - { - terminals.add(terminal); - return this; - } - - - public Terminal peek() - { - return terminals.peek(); - } - - - public Terminal peekLast() - { - return terminals.peekLast(); - } - - - public Terminal pop() - { - return terminals.pop(); - } - - - public TerminalSeqf seal() - { - return new TerminalSeqf(terminals); - } - - - public It getTerminals() - { - return new It(terminals); - } - - - public int size() - { - return terminals.size(); - } - - - public boolean isEmtpy() - { - return terminals.isEmpty(); - } - - - public Iterator iterator() - { - return terminals.iterator(); - } - - - protected int calcHashCode() - { - final int prime = 31; - int result = 1; - result = prime * result + ((terminals == null) ? 0 : terminals.hashCode()); - return result; - } - - - @Override - public int hashCode() - { - return calcHashCode(); - } - - - @Override - public boolean equals(Object obj) - { - if (this == obj) - return true; - if (obj == null) - return false; - if (!(obj instanceof TerminalSeq)) - return false; - TerminalSeq other = (TerminalSeq) obj; - if (terminals == null) - { - if (other.terminals != null) - return false; - } else { - final Iterator thisIt = this.terminals.iterator(); - final Iterator otherIt = other.terminals.iterator(); - while (thisIt.hasNext() && otherIt.hasNext()) - { - if (!thisIt.next().equals(otherIt.next())) - { - return false; - } - } - return thisIt.hasNext() == otherIt.hasNext(); - } - return true; - } - - - @Override - public String toString() - { - StringBuilder b = new StringBuilder(); - boolean first = true; - for (Terminal t : terminals) - { - if (first) - { - first = false; - } else { - b.append('-'); - } - b.append(t); - } - return b.toString(); - } +public class TerminalSeq implements ITerminalSeq { + private static final long serialVersionUID = -2583663816926783360L; + + protected final LinkedList terminals; + + public TerminalSeq() { + this.terminals = new LinkedList(); + } + + /** + * The sequence is based on the ordering of the collections iterator + * + * @param terminals + */ + public TerminalSeq(Collection terminals) { + this.terminals = new LinkedList(terminals); + } + + /** + * The sequence is based on the ordering of the iterator + * + * @param iterator + */ + public TerminalSeq(Iterator iterator) { + this.terminals = new LinkedList(); + while (iterator.hasNext()) { + this.terminals.add(iterator.next()); + } + } + + /** @param terminals */ + public TerminalSeq(Terminal... terminals) { + this.terminals = new LinkedList(); + for (Terminal terminal : terminals) { + this.terminals.add(terminal); + } + } + + public ITerminalSeq concatenate(ITerminalSeq other) { + for (Terminal terminal : other.getTerminals()) { + terminals.add(terminal); + } + return this; + } + + public ITerminalSeq append(Terminal terminal) { + terminals.add(terminal); + return this; + } + + public Terminal peek() { + return terminals.peek(); + } + + public Terminal peekLast() { + return terminals.peekLast(); + } + + public Terminal pop() { + return terminals.pop(); + } + + public TerminalSeqf seal() { + return new TerminalSeqf(terminals); + } + + public It getTerminals() { + return new It(terminals); + } + + public int size() { + return terminals.size(); + } + + public boolean isEmtpy() { + return terminals.isEmpty(); + } + + public Iterator iterator() { + return terminals.iterator(); + } + + protected int calcHashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((terminals == null) ? 0 : terminals.hashCode()); + return result; + } + + @Override + public int hashCode() { + return calcHashCode(); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (!(obj instanceof TerminalSeq)) return false; + TerminalSeq other = (TerminalSeq) obj; + if (terminals == null) { + if (other.terminals != null) return false; + } else { + final Iterator thisIt = this.terminals.iterator(); + final Iterator otherIt = other.terminals.iterator(); + while (thisIt.hasNext() && otherIt.hasNext()) { + if (!Objects.equals(thisIt.next(), otherIt.next())) { + return false; + } + } + return thisIt.hasNext() == otherIt.hasNext(); + } + return true; + } + + @Override + public String toString() { + StringBuilder b = new StringBuilder(); + boolean first = true; + for (Terminal t : terminals) { + if (first) { + first = false; + } else { + b.append('-'); + } + b.append(t); + } + return b.toString(); + } } diff --git a/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/grammar/CheckedGrammar.java b/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/grammar/CheckedGrammar.java index 0977b5db9..29b3993e7 100644 --- a/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/grammar/CheckedGrammar.java +++ b/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/grammar/CheckedGrammar.java @@ -1,540 +1,448 @@ package edu.tum.cup2.grammar; -import java.util.Hashtable; +import edu.tum.cup2.util.ArrayTools; +import java.util.Collection; import java.util.HashSet; +import java.util.Hashtable; import java.util.LinkedList; -import java.util.Collection; import java.util.List; import java.util.Map.Entry; - -import edu.tum.cup2.util.ArrayTools; +import java.util.Objects; /** - * CheckedGrammar - * also has additional information (reachability and productivity) for symbols, - * - * CheckedGrammar is a decorator for Grammars + * CheckedGrammar also has additional information (reachability and productivity) for symbols, + * + *

CheckedGrammar is a decorator for Grammars * * @author Michael Hausmann * @author Dangl Stefan - * */ -public class CheckedGrammar implements IGrammar -{ - public static final long serialVersionUID = 1L; - private final IGrammar grammar; - private Hashtable symbolInfos = - new Hashtable(); - - //grammar doesn't change therefore checking the productivity and reachability once is enough - private boolean bProductivityChecked = false; - private boolean bReachabilityChecked = false; - private final boolean DEBUG = false; - - /** - * Creates a new {@link CheckedGrammar}, using the given Grammar - */ - public CheckedGrammar(IGrammar g) - { - grammar = g; - } - - /** - * Creates a new {@link Grammar}, using the given list of - * {@link Production}s. The first production is the start - * production of the grammar. - */ - public CheckedGrammar(LinkedList terminals, LinkedList nonTerminals, LinkedList productions) - { - grammar = new Grammar(terminals, nonTerminals, productions); - } - - /** - * Creates a new {@link Grammar}, using the given list of - * {@link Production}s. The first production is the start - * production of the grammar. - */ - public CheckedGrammar(Terminal[] terminals, NonTerminal[] nonTerminals, Production[] productions) - { - this(ArrayTools.toLinkedListT(terminals), - ArrayTools.toLinkedListT(nonTerminals), - ArrayTools.toLinkedListT(productions)); - } - - /** - * SymbolInfo - * stores additional information to symbols in the grammar - * e.g: is a NonTerminal reachable? - * - * @author Michael Hausmann - * - */ - private class SymbolInfo - { - public Symbol symbol = null; //symbol that a SymbolInfo refers to - public boolean bReachable = false; - public HashSet producedNonTerminalsSet = new HashSet(); - public boolean bProductive = false; - - public int hashCode() - { - return symbol.hashCode(); - } - - /** - * SymbolInfos are equal if they belong to same symbol - */ - public boolean equals(Object obj) - { - if(obj == null) return false; - if(!(obj instanceof SymbolInfo)) return false; - SymbolInfo otherSymbolInfo = (SymbolInfo) obj; - return (this.symbol.equals(otherSymbolInfo.symbol)); - } - } /* end of class */ - - /** - * creates the dependency graph for NonTerminals - */ - private void createProducedNonTerminalInfo() - { - LinkedList prods = this.getProductions(); //all productions - for(Production prod : prods) - { - SymbolInfo i = this.symbolInfos.get(prod.getLHS()); - if(i != null) - { - List rhsSymbols = prod.getRHS(); - for(Symbol rhsSymbol : rhsSymbols ) - { - if(rhsSymbol instanceof NonTerminal) - { - NonTerminal nt = (NonTerminal) rhsSymbol; - - //for every production add every NonTerminal on the rhs to the SymbolInfo of the lhs - i.producedNonTerminalsSet.add(nt); - } - } - } - } - } - - /** - * set NonTerminal lhs reachable - * and also set dependent symbols reachable recursively - * @param lhs - */ - private void setReachable(NonTerminal lhs) - { - SymbolInfo i = this.symbolInfos.get(lhs); - if(i.bReachable == true) return; //exit loops - i.bReachable = true; - for(NonTerminal nt : i.producedNonTerminalsSet) - { - setReachable(nt); - } - } - - /** - * method to check if all NonTerminals in the grammar are reachable - */ - protected void checkReachability() - { - //grammar doesn't change therefore checking the productivity and reachability once is enough - if(this.bReachabilityChecked) return; - - //get all NonTerminals - LinkedList ntList = getNonTerminals(); - - //create a SymbolInfo for every NonTerminal - for(NonTerminal nt : ntList) - { - if(nt == null) continue; - if(!this.symbolInfos.containsKey(nt)) - { - SymbolInfo i = new SymbolInfo(); - i.symbol = nt; - this.symbolInfos.put(nt, i); - } - } - - createProducedNonTerminalInfo(); - - //check reachability with depth first search starting with the start symbol - Production startProd = this.getStartProduction(); - NonTerminal startState = startProd.getLHS(); - setReachable(startState); - - this.bReachabilityChecked = true; - } - - /** - * getReachableNonTerminals - * returns a LinkedList of reachable NonTerminals - * @return list of reachable NonTerminals - */ - public LinkedList getReachableNonTerminals() - { - this.checkReachability(); - - LinkedList ret = new LinkedList(); - Collection c = this.symbolInfos.values(); - for(SymbolInfo i : c) - { - if((i.symbol instanceof NonTerminal) - && i.bReachable) - { - ret.add((NonTerminal) i.symbol); - } - } - return ret; - } - - /** - * getNotReachableNonTerminals - * returns a LinkedList of n o t reachable NonTerminals - * @return list of reachable NonTerminals - */ - public LinkedList getNotReachableNonTerminals() - { - this.checkReachability(); - - LinkedList ret = new LinkedList(); - Collection c = this.symbolInfos.values(); - for(SymbolInfo i : c) - { - if((i.symbol instanceof NonTerminal) - && !i.bReachable) - { - ret.add((NonTerminal) i.symbol); - } - } - return ret; - } - - /** - * convenience method to query if a grammar has n o t reachable NonTerminals - * @return - */ - public boolean hasNotReachableNonTerminals() - { - this.checkReachability(); - Collection c = this.symbolInfos.values(); - for(SymbolInfo i : c) - { - if((i.symbol instanceof NonTerminal) - && !i.bReachable) - { - return true; - } - } - return false; - } - - /** - * convenience method to query if a grammar has n o t productive NonTerminals - * @return - */ - public boolean hasNotProductiveNonTerminals() - { - this.checkProductivity(); - Collection c = this.symbolInfos.values(); - for(SymbolInfo i : c) - { - if((i.symbol instanceof NonTerminal) - && !i.bProductive) - { - return true; - } - } - return false; - } - - /** - * convenience method to query if the corresponding original grammar is reduced - * @return true if the grammar is reduced; i.e: has no unreachable and no unproductive NonTerminals - */ - public boolean isReduced() - { - return ( !hasNotReachableNonTerminals() && !hasNotProductiveNonTerminals() ); - } - - /** - * getProductiveNonTerminals - * returns a LinkedList of productive NonTerminals - * @return list of productive NonTerminals - */ - public LinkedList getProductiveNonTerminals() - { - this.checkProductivity(); - - LinkedList ret = new LinkedList(); - Collection c = this.symbolInfos.values(); - for(SymbolInfo i : c) - { - if((i.symbol instanceof NonTerminal) - && i.bProductive) - { - ret.add((NonTerminal) i.symbol); - } - } - return ret; - } - - /** - * getNotProductiveNonTerminals - * returns a LinkedList of n o t productive NonTerminals - * @return list of productive NonTerminals - */ - public LinkedList getNotProductiveNonTerminals() - { - this.checkProductivity(); - - LinkedList ret = new LinkedList(); - Collection c = this.symbolInfos.values(); - for(SymbolInfo i : c) - { - if((i.symbol instanceof NonTerminal) - && !i.bProductive) - { - ret.add((NonTerminal) i.symbol); - } - } - return ret; - } - - /** - * getProductiveAndReachableNonTerminals - * returns a LinkedList of productive and reachable NonTerminals - * @return list of productive and reachable NonTerminals - */ - public LinkedList getProductiveAndReachableNonTerminals() - { - this.checkReachability(); - this.checkProductivity(); - - LinkedList ret = new LinkedList(); - Collection c = this.symbolInfos.values(); - for(SymbolInfo i : c) - { - if((i.symbol instanceof NonTerminal) - && i.bProductive - && i.bReachable) - { - ret.add((NonTerminal) i.symbol); - } - } - return ret; - } - - /** checkProductivity - * checks the productivity of each NonTerminal using the - * algorithm from the compiler construction lecture (page 92). - * (fixed point iteration) - */ - protected void checkProductivity() - { - //grammar doesn't change therefore checking the productivity and reachability once is enough - if (this.bProductivityChecked) return; - - //result set containing PRODUCTIVE NTs - List result = new LinkedList(); - - //set of all productions - List P = this.getProductions(); - - //how many NonTerminals (potentially not productive ones) does a production contain? - Hashtable count = new Hashtable(); - - //set of all NTs - List N = this.getNonTerminals(); - - //rhs: mapping from NT to right hand sides in which it occurs - //NOTE: if NT occurs multiple times in one production, then the list contains the productions multiple times - Hashtable> rhs = - new Hashtable>(); - - //Initialization - for(NonTerminal A : N) - { - rhs.put(A, new LinkedList()); - } - for(Production Ai : P) - { - int iNumberOfNonTerminals = 0; - for(Symbol s : Ai.getRHS()) - { - if(s instanceof NonTerminal) - { - iNumberOfNonTerminals++; - LinkedList productionsWithSonRhs = rhs.get(s); - productionsWithSonRhs.addFirst(Ai); - } - } - count.put(Ai, iNumberOfNonTerminals); - } - - //create Workset - LinkedList W = new LinkedList(); - for(Entry r : count.entrySet()) - { - if(r.getValue().equals(0)) - { - if (DEBUG) - System.out.println("working set entry: "+r.getKey()); - W.add(r.getKey()); - } - } - - while(W.size() > 0) - { - Production Ai = W.removeFirst(); //extract one rule of a productive NonTerminal from the Workset - NonTerminal A = Ai.getLHS(); - if( !(result.contains(A)) ) - { - result.add(A); - if (DEBUG ) - System.out.println(A+" productive"); - for(Production r : rhs.get(A)) //get productions that contain the productive NT A in their rhs - { - Integer i = count.get(r); - assert(i != null); //i != null must be valid - i--; //for every occurrence of A decrease the counter - //NOTE: if production r contains A more than once, this loop body is executed also more than once - - count.put(r, i); - if (DEBUG) - System.out.println(" -> "+r+" ("+i+")"); - if(i == 0) W.add(r); - } - } - } - - //create a SymbolInfo for every NonTerminal - LinkedList ntList = getNonTerminals(); - for(NonTerminal nt : ntList) - { - if(nt == null) continue; - if(!this.symbolInfos.containsKey(nt)) - { - SymbolInfo i = new SymbolInfo(); - i.symbol = nt; - this.symbolInfos.put(nt, i); - } - } - - //copy result - for(NonTerminal nt : result) - { - SymbolInfo i = this.symbolInfos.get(nt); - i.bProductive = true; - } - - this.bProductivityChecked = true; - } - - /** - * returns a reduced grammar - * @return - */ - public IGrammar getReducedGrammar() - { - this.checkProductivity(); - this.checkReachability(); - LinkedList requiredNonTerminals = this.getProductiveAndReachableNonTerminals(); - LinkedList allProductions = this.getProductions(); - LinkedList reducedProductions = new LinkedList(allProductions); - for(Production p : allProductions) - { - boolean bKeepProduction = true; - NonTerminal lhs = p.getLHS(); - List rhs = p.getRHS(); - if(!requiredNonTerminals.contains(lhs)) - bKeepProduction = false; - else - { - for(Symbol s : rhs) - { - if(s instanceof NonTerminal) - { - NonTerminal nt = (NonTerminal) s; - if(!requiredNonTerminals.contains(nt)) - { - bKeepProduction = false; - } - } - } /*end for*/ - } - if(!bKeepProduction) - { - reducedProductions.remove(p); - } - } /*end for*/ - - //creating a new Grammar without not required productions - IGrammar ret = new Grammar(this.getTerminals(), requiredNonTerminals, reducedProductions); - return ret; - } - - public IGrammar extendByAuxStartProduction() - { - if(grammar == null) return null; - return this.grammar.extendByAuxStartProduction(); - } - - /** - * Gets the terminals. - */ - public LinkedList getTerminals() - { - if(grammar == null) return null; - return this.grammar.getTerminals(); - } - - /** - * Gets the non-terminals. - */ - public LinkedList getNonTerminals() - { - if(grammar == null) return null; - return this.grammar.getNonTerminals(); - } - - /** - * Gets the number of productions. - */ - public int getProductionCount() - { - if(grammar == null) return 0; - return this.grammar.getProductionCount(); - } - - - /** - * Gets the start production. - */ - public Production getStartProduction() - { - if(grammar == null) return null; - return this.grammar.getStartProduction(); - } - - /** - * Gets the production with the given index. - */ - public Production getProductionAt(int index) - { - if(grammar == null) return null; - return this.grammar.getProductionAt(index); - } - - /** - * Gets the productions. TIDY - */ - public LinkedList getProductions() - { - if(grammar == null) return null; - return this.grammar.getProductions(); - } - -} \ No newline at end of file +public class CheckedGrammar implements IGrammar { + public static final long serialVersionUID = 1L; + private final IGrammar grammar; + private Hashtable symbolInfos = new Hashtable(); + + // grammar doesn't change therefore checking the productivity and reachability once is enough + private boolean bProductivityChecked = false; + private boolean bReachabilityChecked = false; + private final boolean DEBUG = false; + + /** Creates a new {@link CheckedGrammar}, using the given Grammar */ + public CheckedGrammar(IGrammar g) { + grammar = g; + } + + /** + * Creates a new {@link Grammar}, using the given list of {@link Production}s. The first + * production is the start production of the grammar. + */ + public CheckedGrammar( + LinkedList terminals, + LinkedList nonTerminals, + LinkedList productions) { + grammar = new Grammar(terminals, nonTerminals, productions); + } + + /** + * Creates a new {@link Grammar}, using the given list of {@link Production}s. The first + * production is the start production of the grammar. + */ + public CheckedGrammar( + Terminal[] terminals, NonTerminal[] nonTerminals, Production[] productions) { + this( + ArrayTools.toLinkedListT(terminals), + ArrayTools.toLinkedListT(nonTerminals), + ArrayTools.toLinkedListT(productions)); + } + + /** + * SymbolInfo stores additional information to symbols in the grammar e.g: is a NonTerminal + * reachable? + * + * @author Michael Hausmann + */ + private class SymbolInfo { + public Symbol symbol = null; // symbol that a SymbolInfo refers to + public boolean bReachable = false; + public HashSet producedNonTerminalsSet = new HashSet(); + public boolean bProductive = false; + + public int hashCode() { + return symbol.hashCode(); + } + + /** SymbolInfos are equal if they belong to same symbol */ + public boolean equals(Object obj) { + if (obj == null) return false; + if (!(obj instanceof SymbolInfo)) return false; + SymbolInfo otherSymbolInfo = (SymbolInfo) obj; + return (Objects.equals(this.symbol, otherSymbolInfo.symbol)); + } + } /* end of class */ + + /** creates the dependency graph for NonTerminals */ + private void createProducedNonTerminalInfo() { + LinkedList prods = this.getProductions(); // all productions + for (Production prod : prods) { + SymbolInfo i = this.symbolInfos.get(prod.getLHS()); + if (i != null) { + List rhsSymbols = prod.getRHS(); + for (Symbol rhsSymbol : rhsSymbols) { + if (rhsSymbol instanceof NonTerminal) { + NonTerminal nt = (NonTerminal) rhsSymbol; + + // for every production add every NonTerminal on the rhs to the SymbolInfo of the lhs + i.producedNonTerminalsSet.add(nt); + } + } + } + } + } + + /** + * set NonTerminal lhs reachable and also set dependent symbols reachable recursively + * + * @param lhs + */ + private void setReachable(NonTerminal lhs) { + SymbolInfo i = this.symbolInfos.get(lhs); + if (i.bReachable == true) return; // exit loops + i.bReachable = true; + for (NonTerminal nt : i.producedNonTerminalsSet) { + setReachable(nt); + } + } + + /** method to check if all NonTerminals in the grammar are reachable */ + protected void checkReachability() { + // grammar doesn't change therefore checking the productivity and reachability once is enough + if (this.bReachabilityChecked) return; + + // get all NonTerminals + LinkedList ntList = getNonTerminals(); + + // create a SymbolInfo for every NonTerminal + for (NonTerminal nt : ntList) { + if (nt == null) continue; + if (!this.symbolInfos.containsKey(nt)) { + SymbolInfo i = new SymbolInfo(); + i.symbol = nt; + this.symbolInfos.put(nt, i); + } + } + + createProducedNonTerminalInfo(); + + // check reachability with depth first search starting with the start symbol + Production startProd = this.getStartProduction(); + NonTerminal startState = startProd.getLHS(); + setReachable(startState); + + this.bReachabilityChecked = true; + } + + /** + * getReachableNonTerminals returns a LinkedList of reachable NonTerminals + * + * @return list of reachable NonTerminals + */ + public LinkedList getReachableNonTerminals() { + this.checkReachability(); + + LinkedList ret = new LinkedList(); + Collection c = this.symbolInfos.values(); + for (SymbolInfo i : c) { + if ((i.symbol instanceof NonTerminal) && i.bReachable) { + ret.add((NonTerminal) i.symbol); + } + } + return ret; + } + + /** + * getNotReachableNonTerminals returns a LinkedList of n o t reachable NonTerminals + * + * @return list of reachable NonTerminals + */ + public LinkedList getNotReachableNonTerminals() { + this.checkReachability(); + + LinkedList ret = new LinkedList(); + Collection c = this.symbolInfos.values(); + for (SymbolInfo i : c) { + if ((i.symbol instanceof NonTerminal) && !i.bReachable) { + ret.add((NonTerminal) i.symbol); + } + } + return ret; + } + + /** + * convenience method to query if a grammar has n o t reachable NonTerminals + * + * @return + */ + public boolean hasNotReachableNonTerminals() { + this.checkReachability(); + Collection c = this.symbolInfos.values(); + for (SymbolInfo i : c) { + if ((i.symbol instanceof NonTerminal) && !i.bReachable) { + return true; + } + } + return false; + } + + /** + * convenience method to query if a grammar has n o t productive NonTerminals + * + * @return + */ + public boolean hasNotProductiveNonTerminals() { + this.checkProductivity(); + Collection c = this.symbolInfos.values(); + for (SymbolInfo i : c) { + if ((i.symbol instanceof NonTerminal) && !i.bProductive) { + return true; + } + } + return false; + } + + /** + * convenience method to query if the corresponding original grammar is reduced + * + * @return true if the grammar is reduced; i.e: has no unreachable and no unproductive + * NonTerminals + */ + public boolean isReduced() { + return (!hasNotReachableNonTerminals() && !hasNotProductiveNonTerminals()); + } + + /** + * getProductiveNonTerminals returns a LinkedList of productive NonTerminals + * + * @return list of productive NonTerminals + */ + public LinkedList getProductiveNonTerminals() { + this.checkProductivity(); + + LinkedList ret = new LinkedList(); + Collection c = this.symbolInfos.values(); + for (SymbolInfo i : c) { + if ((i.symbol instanceof NonTerminal) && i.bProductive) { + ret.add((NonTerminal) i.symbol); + } + } + return ret; + } + + /** + * getNotProductiveNonTerminals returns a LinkedList of n o t productive NonTerminals + * + * @return list of productive NonTerminals + */ + public LinkedList getNotProductiveNonTerminals() { + this.checkProductivity(); + + LinkedList ret = new LinkedList(); + Collection c = this.symbolInfos.values(); + for (SymbolInfo i : c) { + if ((i.symbol instanceof NonTerminal) && !i.bProductive) { + ret.add((NonTerminal) i.symbol); + } + } + return ret; + } + + /** + * getProductiveAndReachableNonTerminals returns a LinkedList of productive and reachable + * NonTerminals + * + * @return list of productive and reachable NonTerminals + */ + public LinkedList getProductiveAndReachableNonTerminals() { + this.checkReachability(); + this.checkProductivity(); + + LinkedList ret = new LinkedList(); + Collection c = this.symbolInfos.values(); + for (SymbolInfo i : c) { + if ((i.symbol instanceof NonTerminal) && i.bProductive && i.bReachable) { + ret.add((NonTerminal) i.symbol); + } + } + return ret; + } + + /** + * checkProductivity checks the productivity of each NonTerminal using the algorithm from the + * compiler construction lecture (page 92). (fixed point iteration) + */ + protected void checkProductivity() { + // grammar doesn't change therefore checking the productivity and reachability once is enough + if (this.bProductivityChecked) return; + + // result set containing PRODUCTIVE NTs + List result = new LinkedList(); + + // set of all productions + List P = this.getProductions(); + + // how many NonTerminals (potentially not productive ones) does a production contain? + Hashtable count = new Hashtable(); + + // set of all NTs + List N = this.getNonTerminals(); + + // rhs: mapping from NT to right hand sides in which it occurs + // NOTE: if NT occurs multiple times in one production, then the list contains the productions + // multiple times + Hashtable> rhs = + new Hashtable>(); + + // Initialization + for (NonTerminal A : N) { + rhs.put(A, new LinkedList()); + } + for (Production Ai : P) { + int iNumberOfNonTerminals = 0; + for (Symbol s : Ai.getRHS()) { + if (s instanceof NonTerminal) { + iNumberOfNonTerminals++; + LinkedList productionsWithSonRhs = rhs.get(s); + productionsWithSonRhs.addFirst(Ai); + } + } + count.put(Ai, iNumberOfNonTerminals); + } + + // create Workset + LinkedList W = new LinkedList(); + for (Entry r : count.entrySet()) { + if (Objects.equals(r.getValue(), 0)) { + if (DEBUG) System.out.println("working set entry: " + r.getKey()); + W.add(r.getKey()); + } + } + + while (W.size() > 0) { + Production Ai = + W.removeFirst(); // extract one rule of a productive NonTerminal from the Workset + NonTerminal A = Ai.getLHS(); + if (!(result.contains(A))) { + result.add(A); + if (DEBUG) System.out.println(A + " productive"); + for (Production r : + rhs.get(A)) // get productions that contain the productive NT A in their rhs + { + Integer i = count.get(r); + assert (i != null); // i != null must be valid + i--; // for every occurrence of A decrease the counter + // NOTE: if production r contains A more than once, this loop body is executed also more + // than once + + count.put(r, i); + if (DEBUG) System.out.println(" -> " + r + " (" + i + ")"); + if (i == 0) W.add(r); + } + } + } + + // create a SymbolInfo for every NonTerminal + LinkedList ntList = getNonTerminals(); + for (NonTerminal nt : ntList) { + if (nt == null) continue; + if (!this.symbolInfos.containsKey(nt)) { + SymbolInfo i = new SymbolInfo(); + i.symbol = nt; + this.symbolInfos.put(nt, i); + } + } + + // copy result + for (NonTerminal nt : result) { + SymbolInfo i = this.symbolInfos.get(nt); + i.bProductive = true; + } + + this.bProductivityChecked = true; + } + + /** + * returns a reduced grammar + * + * @return + */ + public IGrammar getReducedGrammar() { + this.checkProductivity(); + this.checkReachability(); + LinkedList requiredNonTerminals = this.getProductiveAndReachableNonTerminals(); + LinkedList allProductions = this.getProductions(); + LinkedList reducedProductions = new LinkedList(allProductions); + for (Production p : allProductions) { + boolean bKeepProduction = true; + NonTerminal lhs = p.getLHS(); + List rhs = p.getRHS(); + if (!requiredNonTerminals.contains(lhs)) bKeepProduction = false; + else { + for (Symbol s : rhs) { + if (s instanceof NonTerminal) { + NonTerminal nt = (NonTerminal) s; + if (!requiredNonTerminals.contains(nt)) { + bKeepProduction = false; + } + } + } /*end for*/ + } + if (!bKeepProduction) { + reducedProductions.remove(p); + } + } /*end for*/ + + // creating a new Grammar without not required productions + IGrammar ret = new Grammar(this.getTerminals(), requiredNonTerminals, reducedProductions); + return ret; + } + + public IGrammar extendByAuxStartProduction() { + if (grammar == null) return null; + return this.grammar.extendByAuxStartProduction(); + } + + /** Gets the terminals. */ + public LinkedList getTerminals() { + if (grammar == null) return null; + return this.grammar.getTerminals(); + } + + /** Gets the non-terminals. */ + public LinkedList getNonTerminals() { + if (grammar == null) return null; + return this.grammar.getNonTerminals(); + } + + /** Gets the number of productions. */ + public int getProductionCount() { + if (grammar == null) return 0; + return this.grammar.getProductionCount(); + } + + /** Gets the start production. */ + public Production getStartProduction() { + if (grammar == null) return null; + return this.grammar.getStartProduction(); + } + + /** Gets the production with the given index. */ + public Production getProductionAt(int index) { + if (grammar == null) return null; + return this.grammar.getProductionAt(index); + } + + /** Gets the productions. TIDY */ + public LinkedList getProductions() { + if (grammar == null) return null; + return this.grammar.getProductions(); + } +} diff --git a/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/grammar/Production.java b/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/grammar/Production.java index 88aa4df74..9defcd5d3 100644 --- a/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/grammar/Production.java +++ b/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/grammar/Production.java @@ -1,5 +1,8 @@ package edu.tum.cup2.grammar; +import edu.tum.cup2.semantics.Action; +import edu.tum.cup2.spec.CUP2Specification; +import edu.tum.cup2.util.It; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; @@ -8,377 +11,290 @@ import java.lang.reflect.InvocationTargetException; import java.util.Arrays; import java.util.List; - -import edu.tum.cup2.semantics.Action; -import edu.tum.cup2.spec.CUP2Specification; -import edu.tum.cup2.util.It; - +import java.util.Objects; /** - * A production consists of a left hand side {@link NonTerminal} and a list of right hand side {@link Symbol}s. - * There may be on single {@link Action} which is performed when - * the production is reduced. - * - * If there are productions with more than one right hand sides - * (in parser books separated by "|"), each of these sides - * must be represented by its own {@link Production} instance. - * - * A production should have an index, to support debugging and - * parsing table dumps. - * - * TODO: TIDY constructors - * + * A production consists of a left hand side {@link NonTerminal} and a list of right hand side + * {@link Symbol}s. There may be on single {@link Action} which is performed when the production is + * reduced. + * + *

If there are productions with more than one right hand sides (in parser books separated by + * "|"), each of these sides must be represented by its own {@link Production} instance. + * + *

A production should have an index, to support debugging and parsing table dumps. + * + *

TODO: TIDY constructors + * * @author Andreas Wenger * @author Michael Hausmann * @author Stefan Dangl */ -public final class Production implements Serializable -{ - private static final long serialVersionUID = 2L; - - private int id; - private NonTerminal lhs; - private List rhs; - private Action reduceAction; - - // cache - private Terminal lastTerminal; - private Terminal precTerminal; // save the Terminal with the precedence of this Production - private int rhsSizeWithoutEpsilon; - private int hashCode; - - - /** - * Creates a new {@link Production}. - */ - public Production(int id, NonTerminal lhs, List rhs, Action reduceAction, Terminal precTerminal) - { - // check rhs - if (rhs.size() == 0) - { - // empty RHS means epsilon - rhs = Arrays.asList((Symbol) SpecialTerminals.Epsilon); - this.rhsSizeWithoutEpsilon = 0; - } else if (rhs.size() == 1) - { - this.rhsSizeWithoutEpsilon = (rhs.get(0) == SpecialTerminals.Epsilon ? 0 : 1); - } else if (rhs.size() > 1) - { - // check, that there is no epsilon in a RHS with at least 2 symbols - for (Symbol symbol : rhs) - { - if (symbol == SpecialTerminals.Epsilon) - { - throw new IllegalArgumentException("Epsilon is only allowed as the single symbol of a RHS!"); - } - } - this.rhsSizeWithoutEpsilon = rhs.size(); - } - this.id = id; - this.lhs = lhs; - this.rhs = rhs; - this.reduceAction = reduceAction; - // cache - // find last terminal - Terminal lastTerminal = null; - for (int i = rhs.size() - 1; i >= 0; i--) - { - if (rhs.get(i) instanceof Terminal) - { - lastTerminal = (Terminal) rhs.get(i); - break; - } - } - this.lastTerminal = lastTerminal; - this.precTerminal = (precTerminal != null) ? precTerminal : lastTerminal; - // hash code - computeHashcode(); - } - - - /** - * compute the hashCode for this Production - * @return - */ - private int computeHashcode() - { - int sum = lhs.hashCode(); // default hashCode is the object address - for (Symbol s : rhs) - { - sum = (sum + s.hashCode()) % ((1 << 31) - 1); - } - this.hashCode = sum; - return this.hashCode; - } - - - /** - * Convenience constructor: Just give an ID, a left hand side {@link NonTerminal} and a list of right hand side - * {@link Symbol}s. - * The {@link Production} contains no {@link Action}s. - */ - public Production(int id, NonTerminal lhs, Symbol... rhs) - { - this(id, lhs, Arrays.asList(rhs), null, null); - } - - - /** - * readObject() de-serializes a semantic action - */ - @SuppressWarnings("unchecked") - private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException - { - this.id = (Integer) stream.readObject(); - this.lhs = (NonTerminal) stream.readObject(); - this.rhs = (List) stream.readObject(); - // this.actions = (List) stream.readObject(); - deserializeReduceAction(stream); - this.lastTerminal = (Terminal) stream.readObject(); - this.rhsSizeWithoutEpsilon = (Integer) stream.readObject(); - this.hashCode = (Integer) stream.readObject(); - } - - - @SuppressWarnings("unchecked") - private void deserializeReduceAction(ObjectInputStream stream) throws IOException, ClassNotFoundException - { - try - { - Object outer = null; // helper variable for specification which this production belongs to - Class clazz = (Class) stream.readObject(); - - // if clazz is null then add the null entry to the actions list. - // this guarantees that the actions list is completely restored. - // attention: when clazz is null, the loop continues with the next iteration - if (clazz == null) - { - reduceAction = null; - return; - } - // System.out.println("clazz : "+clazz); - Constructor c_enclosing = (Constructor) clazz.getEnclosingConstructor(); // gets - // constructor - // for - // specification - // System.out.println("constructor enclosing : "+c_enclosing); - Class clazz_enclosing = (Class) clazz.getEnclosingClass(); - if (c_enclosing == null) - { - int min = Integer.MAX_VALUE; - for (Constructor c : clazz_enclosing.getDeclaredConstructors()) - { - Class[] cParms = c.getParameterTypes(); - if (cParms.length <= min) - { - min = cParms.length; - c_enclosing = (Constructor) c; - break; - } - } - } - // System.out.println("clazz enclosing : "+clazz_enclosing); - Constructor[] c_declared = clazz.getDeclaredConstructors(); // 1 constructor - NOT public, NOT default (expects - // reference to outer object) - for (Constructor c : c_declared) - { - Class[] cParms = c.getParameterTypes(); - if (cParms.length == 1) - { - if (cParms[0].equals(clazz_enclosing)) - { - // creating a new instance of Specification is ugly, if Specification should be serialized: - // after de-serialization actions belong to a different specification instance - // this could lead to errors, if actions reference fields in the spec - if (outer == null) - { - Object[] args = new Object[c_enclosing.getParameterTypes().length]; - outer = c_enclosing.newInstance(args); - } - - // Problem: constructor c needs to be public in order to be accessed from here - c.setAccessible(true); - - Action a = (Action) c.newInstance(outer); - reduceAction = a; - } - } - } - - } catch (IllegalAccessException e) - { - e.printStackTrace(); - System.err.println(e.getMessage()); - throw new IOException(); - } catch (InstantiationException e) - { - e.printStackTrace(); - System.err.println(e.getMessage()); - throw new IOException(); - } catch (InvocationTargetException tie) - { - throw new IOException(); - } - } - - - /** - * writes fields to file - */ - private synchronized void writeObject(ObjectOutputStream stream) throws IOException - { - stream.writeObject(this.id); - stream.writeObject(this.lhs); - stream.writeObject(this.rhs); - this.serializeReduceAction(stream); - stream.writeObject(this.lastTerminal); - stream.writeObject(this.rhsSizeWithoutEpsilon); - stream.writeObject(this.hashCode); - } - - - private void serializeReduceAction(ObjectOutputStream stream) throws IOException - { - if (reduceAction != null) - { - @SuppressWarnings("unchecked") - Class c = (Class) reduceAction.getClass(); - stream.writeObject(c); - } else - { - stream.writeObject(null); - } - } - - - /** - * Gets the ID of this production. - */ - public int getID() - { - return id; - } - - - /** - * Gets the left hand side non-terminal. - */ - public NonTerminal getLHS() - { - return lhs; - } - - - /** - * Returns a list of the right hand side symbols. - */ - public List getRHS() - { - return rhs; - } - - - /** - * Returns the number of right hand side symbols, - * without epsilons. - */ - public int getRHSSizeWithoutEpsilon() - { - return rhsSizeWithoutEpsilon; - } - - - /** - * Gets the semantic action assigned to the given position. - * If there is no action, null is returned. - * - * For example, 0 is the action before shifting the first symbol, - * and getRHS().getSize() is the action when the production is reduced. - * - * @deprecated - * - */ - @Deprecated - public Action getAction(int position) - { - if (position < this.rhs.size()) - return null; - else - return reduceAction; - } - - - public Action getReduceAction() - { - return reduceAction; - } - - - /** - * Returns the last terminal within this production or null if there is none. - */ - public Terminal getLastTerminal() - { - return lastTerminal; - } - - - /** - * Returns the Terminal responsible for the precedence rating of this production, or null if there is none. - */ - public Terminal getPrecedenceTerminal() - { - return precTerminal; - } - - - @Override - public boolean equals(Object obj) - { - if (obj instanceof Production) - { - Production p = (Production) obj; - if (lhs != p.lhs) - return false; - if (!rhs.equals(p.rhs)) - return false; - return true; - } - return false; - } - - - @Override - public int hashCode() - { - return hashCode; - } - - - @Override - public String toString() - { - StringBuilder rhsString = new StringBuilder(); - for (Symbol s : rhs) - rhsString.append(s + " "); - return "Production " + id + ": " + lhs + " → " + rhsString; - } - - - public String toString(int dotPosition) - { - StringBuilder rhsString = new StringBuilder(); - It rhsSymbols = new It(rhs); - for (Symbol s : rhsSymbols) - { - if (dotPosition == rhsSymbols.getIndex()) - rhsString.append(". "); - rhsString.append(s + " "); - } - if (dotPosition == rhs.size()) - rhsString.append(". "); - return "Production " + id + ": " + lhs + " → " + rhsString; - } - - +public final class Production implements Serializable { + private static final long serialVersionUID = 2L; + + private int id; + private NonTerminal lhs; + private List rhs; + private Action reduceAction; + + // cache + private Terminal lastTerminal; + private Terminal precTerminal; // save the Terminal with the precedence of this Production + private int rhsSizeWithoutEpsilon; + private int hashCode; + + /** Creates a new {@link Production}. */ + public Production( + int id, NonTerminal lhs, List rhs, Action reduceAction, Terminal precTerminal) { + // check rhs + if (rhs.size() == 0) { + // empty RHS means epsilon + rhs = Arrays.asList((Symbol) SpecialTerminals.Epsilon); + this.rhsSizeWithoutEpsilon = 0; + } else if (rhs.size() == 1) { + this.rhsSizeWithoutEpsilon = (rhs.get(0) == SpecialTerminals.Epsilon ? 0 : 1); + } else if (rhs.size() > 1) { + // check, that there is no epsilon in a RHS with at least 2 symbols + for (Symbol symbol : rhs) { + if (symbol == SpecialTerminals.Epsilon) { + throw new IllegalArgumentException( + "Epsilon is only allowed as the single symbol of a RHS!"); + } + } + this.rhsSizeWithoutEpsilon = rhs.size(); + } + this.id = id; + this.lhs = lhs; + this.rhs = rhs; + this.reduceAction = reduceAction; + // cache + // find last terminal + Terminal lastTerminal = null; + for (int i = rhs.size() - 1; i >= 0; i--) { + if (rhs.get(i) instanceof Terminal) { + lastTerminal = (Terminal) rhs.get(i); + break; + } + } + this.lastTerminal = lastTerminal; + this.precTerminal = (precTerminal != null) ? precTerminal : lastTerminal; + // hash code + computeHashcode(); + } + + /** + * compute the hashCode for this Production + * + * @return + */ + private int computeHashcode() { + int sum = lhs.hashCode(); // default hashCode is the object address + for (Symbol s : rhs) { + sum = (sum + s.hashCode()) % ((1 << 31) - 1); + } + this.hashCode = sum; + return this.hashCode; + } + + /** + * Convenience constructor: Just give an ID, a left hand side {@link NonTerminal} and a list of + * right hand side {@link Symbol}s. The {@link Production} contains no {@link Action}s. + */ + public Production(int id, NonTerminal lhs, Symbol... rhs) { + this(id, lhs, Arrays.asList(rhs), null, null); + } + + /** readObject() de-serializes a semantic action */ + @SuppressWarnings("unchecked") + private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { + this.id = (Integer) stream.readObject(); + this.lhs = (NonTerminal) stream.readObject(); + this.rhs = (List) stream.readObject(); + // this.actions = (List) stream.readObject(); + deserializeReduceAction(stream); + this.lastTerminal = (Terminal) stream.readObject(); + this.rhsSizeWithoutEpsilon = (Integer) stream.readObject(); + this.hashCode = (Integer) stream.readObject(); + } + + @SuppressWarnings("unchecked") + private void deserializeReduceAction(ObjectInputStream stream) + throws IOException, ClassNotFoundException { + try { + Object outer = null; // helper variable for specification which this production belongs to + Class clazz = (Class) stream.readObject(); + + // if clazz is null then add the null entry to the actions list. + // this guarantees that the actions list is completely restored. + // attention: when clazz is null, the loop continues with the next iteration + if (clazz == null) { + reduceAction = null; + return; + } + // System.out.println("clazz : "+clazz); + Constructor c_enclosing = + (Constructor) clazz.getEnclosingConstructor(); // gets + // constructor + // for + // specification + // System.out.println("constructor enclosing : "+c_enclosing); + Class clazz_enclosing = + (Class) clazz.getEnclosingClass(); + if (c_enclosing == null) { + int min = Integer.MAX_VALUE; + for (Constructor c : clazz_enclosing.getDeclaredConstructors()) { + Class[] cParms = c.getParameterTypes(); + if (cParms.length <= min) { + min = cParms.length; + c_enclosing = (Constructor) c; + break; + } + } + } + // System.out.println("clazz enclosing : "+clazz_enclosing); + Constructor[] c_declared = + clazz.getDeclaredConstructors(); // 1 constructor - NOT public, NOT default (expects + // reference to outer object) + for (Constructor c : c_declared) { + Class[] cParms = c.getParameterTypes(); + if (cParms.length == 1) { + if (Objects.equals(cParms[0], clazz_enclosing)) { + // creating a new instance of Specification is ugly, if Specification should be + // serialized: + // after de-serialization actions belong to a different specification instance + // this could lead to errors, if actions reference fields in the spec + if (outer == null) { + Object[] args = new Object[c_enclosing.getParameterTypes().length]; + outer = c_enclosing.newInstance(args); + } + + // Problem: constructor c needs to be public in order to be accessed from here + c.setAccessible(true); + + Action a = (Action) c.newInstance(outer); + reduceAction = a; + } + } + } + + } catch (IllegalAccessException e) { + e.printStackTrace(); + System.err.println(e.getMessage()); + throw new IOException(); + } catch (InstantiationException e) { + e.printStackTrace(); + System.err.println(e.getMessage()); + throw new IOException(); + } catch (InvocationTargetException tie) { + throw new IOException(); + } + } + + /** writes fields to file */ + private synchronized void writeObject(ObjectOutputStream stream) throws IOException { + stream.writeObject(this.id); + stream.writeObject(this.lhs); + stream.writeObject(this.rhs); + this.serializeReduceAction(stream); + stream.writeObject(this.lastTerminal); + stream.writeObject(this.rhsSizeWithoutEpsilon); + stream.writeObject(this.hashCode); + } + + private void serializeReduceAction(ObjectOutputStream stream) throws IOException { + if (reduceAction != null) { + @SuppressWarnings("unchecked") + Class c = (Class) reduceAction.getClass(); + stream.writeObject(c); + } else { + stream.writeObject(null); + } + } + + /** Gets the ID of this production. */ + public int getID() { + return id; + } + + /** Gets the left hand side non-terminal. */ + public NonTerminal getLHS() { + return lhs; + } + + /** Returns a list of the right hand side symbols. */ + public List getRHS() { + return rhs; + } + + /** Returns the number of right hand side symbols, without epsilons. */ + public int getRHSSizeWithoutEpsilon() { + return rhsSizeWithoutEpsilon; + } + + /** + * Gets the semantic action assigned to the given position. + * If there is no action, null is returned. + * + * For example, 0 is the action before shifting the first symbol, + * and getRHS().getSize() is the action when the production is reduced. + * + * @deprecated + * + */ + @Deprecated + public Action getAction(int position) { + if (position < this.rhs.size()) return null; + else return reduceAction; + } + + public Action getReduceAction() { + return reduceAction; + } + + /** Returns the last terminal within this production or null if there is none. */ + public Terminal getLastTerminal() { + return lastTerminal; + } + + /** + * Returns the Terminal responsible for the precedence rating of this production, or null if there + * is none. + */ + public Terminal getPrecedenceTerminal() { + return precTerminal; + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof Production) { + Production p = (Production) obj; + if (lhs != p.lhs) return false; + if (!Objects.equals(rhs, p.rhs)) return false; + return true; + } + return false; + } + + @Override + public int hashCode() { + return hashCode; + } + + @Override + public String toString() { + StringBuilder rhsString = new StringBuilder(); + for (Symbol s : rhs) rhsString.append(s + " "); + return "Production " + id + ": " + lhs + " → " + rhsString; + } + + public String toString(int dotPosition) { + StringBuilder rhsString = new StringBuilder(); + It rhsSymbols = new It(rhs); + for (Symbol s : rhsSymbols) { + if (dotPosition == rhsSymbols.getIndex()) rhsString.append(". "); + rhsString.append(s + " "); + } + if (dotPosition == rhs.size()) rhsString.append(". "); + return "Production " + id + ": " + lhs + " → " + rhsString; + } } diff --git a/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/io/GraphBuilderVisitor.java b/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/io/GraphBuilderVisitor.java index 8edc2ac96..88bd15999 100644 --- a/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/io/GraphBuilderVisitor.java +++ b/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/io/GraphBuilderVisitor.java @@ -1,5 +1,8 @@ package edu.tum.cup2.io; +import edu.tum.cup2.generator.Automaton; +import edu.tum.cup2.generator.Edge; +import edu.tum.cup2.generator.states.State; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; @@ -7,258 +10,209 @@ import java.io.IOException; import java.util.HashMap; import java.util.HashSet; - -import edu.tum.cup2.generator.Automaton; -import edu.tum.cup2.generator.Edge; -import edu.tum.cup2.generator.states.State; +import java.util.Objects; import java.util.Set; /** - * GraphBuilderVisitor - * visits the Automaton object and its parts (i.e. edges and states) - * and creates a dot input file for GraphViz - * in order to create a diagram of the automaton - * - * @author Michael Hausmann + * GraphBuilderVisitor visits the Automaton object and its parts (i.e. edges and states) and creates + * a dot input file for GraphViz in order to create a diagram of the automaton * - */ -public class GraphBuilderVisitor implements IAutomatonVisitor -{ - //StringBuilders used to build graphViz input successively - private StringBuilder fsbAcceptingStates; //dot input for accepting states - private StringBuilder fsbNormalStates; //dot input for normal states - private State fStartState; //start state of the automaton - private StringBuilder fsbStartEdges; //start edges as dot input - private StringBuilder fsbEdges; //edges as dot input - private int fCounter; //counter for generating state numbers - private HashMap fStateNumbers; - private HashSet fAppendedNormalStates; - private StringBuilder fLegend; - - /** - * initialize object before visiting new automaton - */ - private void init() - { - fsbAcceptingStates = new StringBuilder(); //dot input for accepting states - fsbNormalStates = new StringBuilder(); //dot input for normal states - fStartState = null; //start state of the automaton - fsbStartEdges = new StringBuilder(); //start edges as dot input - fsbEdges = new StringBuilder(); //edges as dot input - fCounter = 0; //counter for generating state numbers - fStateNumbers = new HashMap(); - fAppendedNormalStates = new HashSet(); - fLegend = new StringBuilder(); - - } - - /** - * Visit an Automaton object - */ - public void visit(Automaton a) - { - init(); - - this.fStartState = a.getStartState(); - Set he = (Set) a.getEdges(); - for(Edge e : he) - { - e.visited(this); - } - //just for debug - System.out.println("visiting automaton structure done."); - System.out.println("#states: " + a.getStates().size() - + " #edges: " + a.getEdges().size()); - } + * @author Michael Hausmann + */ +public class GraphBuilderVisitor implements IAutomatonVisitor { + // StringBuilders used to build graphViz input successively + private StringBuilder fsbAcceptingStates; // dot input for accepting states + private StringBuilder fsbNormalStates; // dot input for normal states + private State fStartState; // start state of the automaton + private StringBuilder fsbStartEdges; // start edges as dot input + private StringBuilder fsbEdges; // edges as dot input + private int fCounter; // counter for generating state numbers + private HashMap fStateNumbers; + private HashSet fAppendedNormalStates; + private StringBuilder fLegend; + + /** initialize object before visiting new automaton */ + private void init() { + fsbAcceptingStates = new StringBuilder(); // dot input for accepting states + fsbNormalStates = new StringBuilder(); // dot input for normal states + fStartState = null; // start state of the automaton + fsbStartEdges = new StringBuilder(); // start edges as dot input + fsbEdges = new StringBuilder(); // edges as dot input + fCounter = 0; // counter for generating state numbers + fStateNumbers = new HashMap(); + fAppendedNormalStates = new HashSet(); + fLegend = new StringBuilder(); + } + + /** Visit an Automaton object */ + public void visit(Automaton a) { + init(); + + this.fStartState = a.getStartState(); + Set he = (Set) a.getEdges(); + for (Edge e : he) { + e.visited(this); + } + // just for debug + System.out.println("visiting automaton structure done."); + System.out.println("#states: " + a.getStates().size() + " #edges: " + a.getEdges().size()); + } + + /** Visit a generic state */ + public void visit(State s) { + if (!this.fAppendedNormalStates.contains(s)) { + appendStateToBuilder(s, this.fsbNormalStates); + this.fAppendedNormalStates.add(s); + } + } + + /** Visit an Edge of the Automaton */ + public void visit(Edge e) { + State s1 = e.getSrc(); + State s2 = e.getDest(); + if (e.isDestAccepting()) { + appendStateToBuilder(s1, this.fsbAcceptingStates); + } + + if (s1 != null && s2 != null) { + // visit states to create long names + s1.visited(this); + s2.visited(this); + + if (Objects.equals(s1, this.fStartState)) { + // draw arrow from start state to target state + fsbStartEdges.append(getStateNumber(s1)); + fsbStartEdges.append(" -> "); + fsbStartEdges.append(getStateNumber(s2)); + fsbStartEdges.append(" [label=\"" + e.getSymbol().toString() + "\"]"); + fsbStartEdges.append(";\n "); + } else { + fsbEdges.append(getStateNumber(s1)); + fsbEdges.append(" -> "); + fsbEdges.append(getStateNumber(s2)); + fsbEdges.append(" [label=\"" + e.getSymbol().toString() + "\"]"); + fsbEdges.append(";\n "); + } + } + } + + /** + * Retrieve the state number for an automaton state if number does not exist yet, generate one and + * append the state and its items to the legend + * + * @param state + * @return state number + */ + private Integer getStateNumber(State s) { + Integer n = fStateNumbers.get(s); + if (n == null) { + n = this.fCounter++; + fStateNumbers.put(s, n); + if (Objects.equals(s, this.fStartState)) fLegend.append("start state (" + n + ")"); + else { + fLegend.append("state "); + fLegend.append(n); + } + fLegend.append(": \n"); + for (Object i : s.getItems()) { + fLegend.append(i.toString()); + fLegend.append("; \n"); + } + fLegend.append("\n"); + } + return n; + } + + /** + * append state info to StringBuilder + * + * @param s + * @param sb + */ + private void appendStateToBuilder(State s, StringBuilder sb) { + Integer n = getStateNumber(s); + sb.append(n); + sb.append(";\n "); + } + + /** save the legend to a file */ + private void saveLegendToFile(String strFileName) { + File f = new File(strFileName); + try { + FileWriter fw = new FileWriter(f); + fw.write(this.fLegend.toString()); + fw.close(); + + } catch (IOException e) { + System.err.println("Could not wirte legend to file: " + strFileName); + e.printStackTrace(); + } + } + + /** + * returns the dot input for GraphViz as String + * + * @return String + */ + protected String buildGraphVizInput() { + StringBuilder fsbResult = new StringBuilder(); + fsbResult.append("digraph cup2Automaton {"); + + // draw all accepting states with double circle + fsbResult.append("node [shape=doublecircle]; "); + fsbResult.append(this.fsbAcceptingStates.toString()); + + // draw all non-accepting states with single circle + fsbResult.append("node [shape=circle]; "); + fsbResult.append(this.fsbNormalStates.toString()); + + fsbResult.append(this.fsbStartEdges.toString()); + fsbResult.append(this.fsbEdges.toString()); + + fsbResult.append("}"); // building the complete graph for the automaton done + + return fsbResult.toString(); + } + + /** + * Saving the dot input for GraphViz to a file + * + * @param strFileName: name of the input file + */ + public void saveGraphVizInput(String strFileName) { + // build the dot input for graphViz + String strInput = buildGraphVizInput(); + + // save the dot file + File f = new File(strFileName); + FileOutputStream fos; + try { + fos = new FileOutputStream(f); + fos.write(strInput.getBytes()); + } catch (FileNotFoundException e) { + System.err.println("File " + f.getAbsolutePath() + " not found."); + e.printStackTrace(); + } catch (IOException e) { + System.err.println("IOException: " + e.getMessage()); + e.printStackTrace(); + } + + // save the description of the state to file + saveLegendToFile(strFileName + ".legend.txt"); - /** - * Visit a generic state - */ - public void visit(State s) - { - if(!this.fAppendedNormalStates.contains(s)) - { - appendStateToBuilder(s, this.fsbNormalStates); - this.fAppendedNormalStates.add(s); - } - } - - /** - * Visit an Edge of the Automaton - */ - public void visit(Edge e) - { - State s1 = e.getSrc(); - State s2 = e.getDest(); - if(e.isDestAccepting()) - { - appendStateToBuilder(s1, this.fsbAcceptingStates); - } - - if(s1 != null && s2 != null) - { - //visit states to create long names - s1.visited(this); - s2.visited(this); - - if(s1.equals(this.fStartState)) - { - //draw arrow from start state to target state - fsbStartEdges.append(getStateNumber(s1)); - fsbStartEdges.append(" -> "); - fsbStartEdges.append(getStateNumber(s2)); - fsbStartEdges.append(" [label=\"" + e.getSymbol().toString() + "\"]"); - fsbStartEdges.append(";\n "); - } - else - { - fsbEdges.append(getStateNumber(s1)); - fsbEdges.append(" -> "); - fsbEdges.append(getStateNumber(s2)); - fsbEdges.append(" [label=\"" + e.getSymbol().toString() + "\"]"); - fsbEdges.append(";\n "); - } - } - } - - /** - * Retrieve the state number for an automaton state - * if number does not exist yet, generate one and - * append the state and its items to the legend - * - * @param state - * @return state number - */ - private Integer getStateNumber(State s) - { - Integer n = fStateNumbers.get(s); - if(n == null) - { - n = this.fCounter++; - fStateNumbers.put(s, n); - if(s.equals(this.fStartState)) - fLegend.append("start state (" + n + ")"); - else - { - fLegend.append("state "); - fLegend.append(n); - } - fLegend.append(": \n"); - for(Object i : s.getItems()) - { - fLegend.append(i.toString()); - fLegend.append("; \n"); - } - fLegend.append("\n"); - } - return n; - } - - /** - * append state info to StringBuilder - * - * @param s - * @param sb - */ - private void appendStateToBuilder(State s, StringBuilder sb) - { - Integer n = getStateNumber(s); - sb.append(n); - sb.append(";\n "); - } - - /** - * save the legend to a file - */ - private void saveLegendToFile(String strFileName) - { - File f = new File(strFileName); - try { - FileWriter fw = new FileWriter(f); - fw.write(this.fLegend.toString()); - fw.close(); - - } catch (IOException e) { - System.err.println("Could not wirte legend to file: " + strFileName); - e.printStackTrace(); - } - - } - - /** - * returns the dot input for GraphViz as String - * @return String - */ - protected String buildGraphVizInput() - { - StringBuilder fsbResult = new StringBuilder(); - fsbResult.append("digraph cup2Automaton {"); - - //draw all accepting states with double circle - fsbResult.append("node [shape=doublecircle]; "); - fsbResult.append(this.fsbAcceptingStates.toString()); - - //draw all non-accepting states with single circle - fsbResult.append("node [shape=circle]; "); - fsbResult.append(this.fsbNormalStates.toString()); - - fsbResult.append(this.fsbStartEdges.toString()); - fsbResult.append(this.fsbEdges.toString()); - - fsbResult.append("}"); //building the complete graph for the automaton done - - return fsbResult.toString(); - } - - /** - * Saving the dot input for GraphViz to a file - * - * @param strFileName: name of the input file - */ - public void saveGraphVizInput(String strFileName) - { - //build the dot input for graphViz - String strInput = buildGraphVizInput(); - - //save the dot file - File f = new File(strFileName); - FileOutputStream fos; - try { - fos = new FileOutputStream(f); - fos.write(strInput.getBytes()); - } catch (FileNotFoundException e) { - System.err.println("File " + f.getAbsolutePath() + " not found."); - e.printStackTrace(); - } catch (IOException e) { - System.err.println("IOException: " + e.getMessage()); - e.printStackTrace(); - } - - //save the description of the state to file - saveLegendToFile(strFileName + ".legend.txt"); - - //create a picture file with dot.exe from the dot input file - try { - File fDotExe = new File("dot"); - if(fDotExe.canExecute()) - { - String[] cmdarray = { - "dot", - "-Tpng", - "-o" + strFileName + ".png", - strFileName - }; - Runtime.getRuntime().exec(cmdarray); - - } - else - { - System.err.println("dot.exe is not accessible! Could not create png file."); - } - } catch (IOException e) { - System.err.println("Could not run dot.exe on created dot input"); - e.printStackTrace(); - } - } + // create a picture file with dot.exe from the dot input file + try { + File fDotExe = new File("dot"); + if (fDotExe.canExecute()) { + String[] cmdarray = {"dot", "-Tpng", "-o" + strFileName + ".png", strFileName}; + Runtime.getRuntime().exec(cmdarray); + } else { + System.err.println("dot.exe is not accessible! Could not create png file."); + } + } catch (IOException e) { + System.err.println("Could not run dot.exe on created dot input"); + e.printStackTrace(); + } + } } diff --git a/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/parser/LLkParser.java b/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/parser/LLkParser.java index 060174541..23694fc5b 100644 --- a/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/parser/LLkParser.java +++ b/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/parser/LLkParser.java @@ -1,12 +1,5 @@ package edu.tum.cup2.parser; -import java.io.IOException; -import java.util.Collection; -import java.util.Deque; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.Stack; - import edu.tum.cup2.generator.items.LLkItem; import edu.tum.cup2.generator.states.LLkState; import edu.tum.cup2.generator.terminals.ITerminalSeq; @@ -24,309 +17,270 @@ import edu.tum.cup2.semantics.Action; import edu.tum.cup2.semantics.ActionPerformer; import edu.tum.cup2.semantics.SymbolValue; - +import java.io.IOException; +import java.util.Collection; +import java.util.Deque; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.Objects; +import java.util.Stack; /** - * This class implements a (strong) LL(k)-Parsing pushdown automaton. Its state is represented by the {@link LLkItem}s - * on the {@link #stack} - or a {@link LLkParserState} containing these one (or two) items. - * + * This class implements a (strong) LL(k)-Parsing pushdown automaton. Its state is represented by + * the {@link LLkItem}s on the {@link #stack} - or a {@link LLkParserState} containing these one (or + * two) items. + * * @author Gero - * */ -public class LLkParser extends AParser -{ - private final boolean DEBUG = false; - - protected final LLkParsingTable table; - private final int k; - - private final Stack stack; - private final Stack valueStack; - - - /** - * @param table - */ - public LLkParser(LLkParsingTable table) - { - this.table = table; - this.k = table.getK(); - - this.stack = new Stack(); - this.valueStack = new Stack(); - } - - - public synchronized Object parse(Scanner input, Object... initArgs) throws IOException, LLkParserException - { - // ### Initialization - // table.getParserInterface().init(this, initArgs); - - // # Prepare parser stack - stack.clear(); - final LLkState startState = table.getStartState(); - stack.push(startState.getFirstItem()); - - // # Prepare semantic stack - valueStack.clear(); - valueStack.push(SymbolValue.NoValue); - - // # Prepare Lookahead - final LinkedList> tokenBuffer = new LinkedList>(); - - - // ### Process input - final LLkItem endItem = table.getEndState().getFirstItem(); - LLkItem currentItem = null; - - ScannerToken currentToken = readNextToken(input, tokenBuffer, k); - - while (stack.size() > 0) - { - // # Init cycle - currentItem = stack.peek(); - - // Check for Accept.. - if (currentItem.equals(endItem)) - { - // # Accept! - if (DEBUG) - { - if (!(currentToken.getSymbol() == SpecialTerminals.EndOfInputStream)) - { - System.err.println("Accepted, but not end of input stream... error?"); - } - - if (stack.size() != 0) - { - System.err - .println("Something went wrong; somehow we think we should accept but the stack is not empty...???"); - } - - System.out.println("Accepted!"); - } - - // table.getParserInterface().exit(this); - - return valueStack.peek(); - } - - - // # What to do? - final ITerminalSeq lookahead = createLookahead(tokenBuffer, k); - - if (!currentItem.isComplete()) - { - // # Push/Expand or Shift/Consume... - // Get new state - final LLkState currentState = getTopMostItems(1); - - // ...? - if (currentItem.getNextSymbol() instanceof NonTerminal) - { - // # Expand! Add both incoming items on top of the stack - final LLkState to = table.get(currentState, SpecialTerminals.Epsilon, lookahead); - checkError(currentToken, to); - - - stack.pop(); - stack.push(to.getSecondItem()); - stack.push(to.getFirstItem()); - - if (DEBUG) - { - System.out.println("Expand to: " + to); - } - } else - { - // # Shift! - final LLkState to = table.get(currentState, currentToken.getSymbol(), lookahead); - checkError(currentToken, to); - - valueStack.push(currentToken.hasValue() ? currentToken.getValue() : SymbolValue.NoValue); - - stack.pop(); - stack.push(to.getFirstItem()); - - if (DEBUG) - { - System.out.println("Consume: " + currentToken.getSymbol() + ", change to: " + to); - } - currentToken = readNextToken(input, tokenBuffer, k); // Consume one input! - } - } else - { - // # Reduce! - final LLkState currentState = getTopMostItems(2); - final LLkState to = table.get(currentState, SpecialTerminals.Epsilon, lookahead); - checkError(currentToken, to); - - // Perform reduce action - final Production currentProduction = currentItem.getProduction(); - final Action reduceAction = currentProduction.getReduceAction(); - Object newValue = SymbolValue.NoValue; - if (reduceAction != null) - { - newValue = ActionPerformer.perform(reduceAction, valueStack, calcRhsSize(currentProduction)); - } - // For each symbol on the right hand side pop one value from the stack - for (int i = 0; i < currentProduction.getRHSSizeWithoutEpsilon(); i++) - { - valueStack.pop(); - } - valueStack.push(newValue); - - stack.pop(); - stack.pop(); - stack.push(to.getFirstItem()); - - if (DEBUG) - { - System.out.println("Reduce to: " + to); - } - } - } - - // Should never happen! - throw new RuntimeException("Incomplete statement: Stack is emtpy but no accept!!!"); - } - - - /** - * TODO Relevant for LL-Parser??? - * - * @param production - * @return Size of RHS - */ - private int calcRhsSize(Production production) - { - int result = production.getRHSSizeWithoutEpsilon(); - if (production.getLHS() instanceof AuxiliaryLHS4SemanticShiftAction) - { - final AuxiliaryLHS4SemanticShiftAction auxAction = (AuxiliaryLHS4SemanticShiftAction) production.getLHS(); - result += auxAction.numPrecedingSymbolsNotEpsilon; - } - return result; - } - - - private void checkError(ScannerToken currentToken, LLkState state) throws LLkParserException - { - if (state == null) - { - throw new LLkParserException("No new state returned from table!"); - } - if (state instanceof LLkErrorState) - { - final LLkErrorState errState = (LLkErrorState) state; - throw new LLkParserException(errState.getMsg()); - } - } - - - private LLkState getTopMostItems(int numOfItems) - { - if (numOfItems < 0 || numOfItems > 2) - { - throw new IllegalArgumentException("NumOfItems must 1 or 2!!!"); - } - - if (!(stack.size() >= numOfItems)) - { - throw new RuntimeException("Stack has less items then expexted! (" + stack.size() + " < " + numOfItems + ")"); - } - - - if (numOfItems == 1) - { - return new LLkState(stack.peek()); - } else if (numOfItems == 2) - { - return new LLkState(stack.get(stack.size() - 2), stack.peek()); - } - - return null; // Logically unreachable!!! - } - - - /** - * Reads a token from the stream and maintains the lookahead and buffered token-list - * - * @param input - * @param tokens - * @param k - * @return The current token! - */ - private ScannerToken readNextToken(final Scanner input, final Deque> tokens, final int k) - { - final int nominalSize = k + 1; // 1 extra for currentToken - - // Remove head in case there is anything to remove - if (!tokens.isEmpty()) - { - tokens.pop(); - } - - // Fill up the buffer - final ScannerToken last = tokens.peekLast(); - if (last == null || last.getSymbol() != SpecialTerminals.EndOfInputStream) - { - try - { - // Empty or there are still tokens left: Read tokens into the buffer until we have enough for lookahead - // creation plus the current symbol - ScannerToken nextToken = null; - do - { - nextToken = input.readNextTerminal(); - // Safety check - if (nextToken == null) - { - throw new IOException("Null-token received! Check your scanner implementation!"); - } - - tokens.add(nextToken); - } while (tokens.size() < nominalSize && nextToken.getSymbol() != SpecialTerminals.EndOfInputStream); - - } catch (IOException e) - { - System.err.println("Error while reading from scanner: " + input); - e.printStackTrace(); - return null; - } - } - - // Keep lookahead and tokens in sync - final ScannerToken currentToken = tokens.peek(); - if (currentToken == null) - { - System.err.println("Tried to read over END_OF_INPUTSTREAM!"); - return null; - } - - // Check for inserted scanner-tokens - if (currentToken instanceof InsertedScannerToken) - { - InsertedScannerToken inserted = (InsertedScannerToken) currentToken; - if (inserted.getErrorInformation() != null) - { - notifyObserversAbout(inserted.getErrorInformation()); - } - } - return currentToken; - } - - - private ITerminalSeq createLookahead(Collection> buffer, final int k) - { - final ITerminalSeq seq = new TerminalSeq(); - - final Iterator> it = buffer.iterator(); - while (seq.size() < k && it.hasNext()) - { - seq.append(it.next().getSymbol()); - } - - return seq; - } +public class LLkParser extends AParser { + private final boolean DEBUG = false; + + protected final LLkParsingTable table; + private final int k; + + private final Stack stack; + private final Stack valueStack; + + /** @param table */ + public LLkParser(LLkParsingTable table) { + this.table = table; + this.k = table.getK(); + + this.stack = new Stack(); + this.valueStack = new Stack(); + } + + public synchronized Object parse(Scanner input, Object... initArgs) + throws IOException, LLkParserException { + // ### Initialization + // table.getParserInterface().init(this, initArgs); + + // # Prepare parser stack + stack.clear(); + final LLkState startState = table.getStartState(); + stack.push(startState.getFirstItem()); + + // # Prepare semantic stack + valueStack.clear(); + valueStack.push(SymbolValue.NoValue); + + // # Prepare Lookahead + final LinkedList> tokenBuffer = new LinkedList>(); + + // ### Process input + final LLkItem endItem = table.getEndState().getFirstItem(); + LLkItem currentItem = null; + + ScannerToken currentToken = readNextToken(input, tokenBuffer, k); + + while (stack.size() > 0) { + // # Init cycle + currentItem = stack.peek(); + + // Check for Accept.. + if (Objects.equals(currentItem, endItem)) { + // # Accept! + if (DEBUG) { + if (!(currentToken.getSymbol() == SpecialTerminals.EndOfInputStream)) { + System.err.println("Accepted, but not end of input stream... error?"); + } + + if (stack.size() != 0) { + System.err.println( + "Something went wrong; somehow we think we should accept but the stack is not empty...???"); + } + + System.out.println("Accepted!"); + } + + // table.getParserInterface().exit(this); + + return valueStack.peek(); + } + + // # What to do? + final ITerminalSeq lookahead = createLookahead(tokenBuffer, k); + + if (!currentItem.isComplete()) { + // # Push/Expand or Shift/Consume... + // Get new state + final LLkState currentState = getTopMostItems(1); + + // ...? + if (currentItem.getNextSymbol() instanceof NonTerminal) { + // # Expand! Add both incoming items on top of the stack + final LLkState to = table.get(currentState, SpecialTerminals.Epsilon, lookahead); + checkError(currentToken, to); + + stack.pop(); + stack.push(to.getSecondItem()); + stack.push(to.getFirstItem()); + + if (DEBUG) { + System.out.println("Expand to: " + to); + } + } else { + // # Shift! + final LLkState to = table.get(currentState, currentToken.getSymbol(), lookahead); + checkError(currentToken, to); + + valueStack.push(currentToken.hasValue() ? currentToken.getValue() : SymbolValue.NoValue); + + stack.pop(); + stack.push(to.getFirstItem()); + + if (DEBUG) { + System.out.println("Consume: " + currentToken.getSymbol() + ", change to: " + to); + } + currentToken = readNextToken(input, tokenBuffer, k); // Consume one input! + } + } else { + // # Reduce! + final LLkState currentState = getTopMostItems(2); + final LLkState to = table.get(currentState, SpecialTerminals.Epsilon, lookahead); + checkError(currentToken, to); + + // Perform reduce action + final Production currentProduction = currentItem.getProduction(); + final Action reduceAction = currentProduction.getReduceAction(); + Object newValue = SymbolValue.NoValue; + if (reduceAction != null) { + newValue = + ActionPerformer.perform(reduceAction, valueStack, calcRhsSize(currentProduction)); + } + // For each symbol on the right hand side pop one value from the stack + for (int i = 0; i < currentProduction.getRHSSizeWithoutEpsilon(); i++) { + valueStack.pop(); + } + valueStack.push(newValue); + + stack.pop(); + stack.pop(); + stack.push(to.getFirstItem()); + + if (DEBUG) { + System.out.println("Reduce to: " + to); + } + } + } + + // Should never happen! + throw new RuntimeException("Incomplete statement: Stack is emtpy but no accept!!!"); + } + + /** + * TODO Relevant for LL-Parser??? + * + * @param production + * @return Size of RHS + */ + private int calcRhsSize(Production production) { + int result = production.getRHSSizeWithoutEpsilon(); + if (production.getLHS() instanceof AuxiliaryLHS4SemanticShiftAction) { + final AuxiliaryLHS4SemanticShiftAction auxAction = + (AuxiliaryLHS4SemanticShiftAction) production.getLHS(); + result += auxAction.numPrecedingSymbolsNotEpsilon; + } + return result; + } + + private void checkError(ScannerToken currentToken, LLkState state) throws LLkParserException { + if (state == null) { + throw new LLkParserException("No new state returned from table!"); + } + if (state instanceof LLkErrorState) { + final LLkErrorState errState = (LLkErrorState) state; + throw new LLkParserException(errState.getMsg()); + } + } + + private LLkState getTopMostItems(int numOfItems) { + if (numOfItems < 0 || numOfItems > 2) { + throw new IllegalArgumentException("NumOfItems must 1 or 2!!!"); + } + + if (!(stack.size() >= numOfItems)) { + throw new RuntimeException( + "Stack has less items then expexted! (" + stack.size() + " < " + numOfItems + ")"); + } + + if (numOfItems == 1) { + return new LLkState(stack.peek()); + } else if (numOfItems == 2) { + return new LLkState(stack.get(stack.size() - 2), stack.peek()); + } + + return null; // Logically unreachable!!! + } + + /** + * Reads a token from the stream and maintains the lookahead and buffered token-list + * + * @param input + * @param tokens + * @param k + * @return The current token! + */ + private ScannerToken readNextToken( + final Scanner input, final Deque> tokens, final int k) { + final int nominalSize = k + 1; // 1 extra for currentToken + + // Remove head in case there is anything to remove + if (!tokens.isEmpty()) { + tokens.pop(); + } + + // Fill up the buffer + final ScannerToken last = tokens.peekLast(); + if (last == null || last.getSymbol() != SpecialTerminals.EndOfInputStream) { + try { + // Empty or there are still tokens left: Read tokens into the buffer until we have enough + // for lookahead + // creation plus the current symbol + ScannerToken nextToken = null; + do { + nextToken = input.readNextTerminal(); + // Safety check + if (nextToken == null) { + throw new IOException("Null-token received! Check your scanner implementation!"); + } + + tokens.add(nextToken); + } while (tokens.size() < nominalSize + && nextToken.getSymbol() != SpecialTerminals.EndOfInputStream); + + } catch (IOException e) { + System.err.println("Error while reading from scanner: " + input); + e.printStackTrace(); + return null; + } + } + + // Keep lookahead and tokens in sync + final ScannerToken currentToken = tokens.peek(); + if (currentToken == null) { + System.err.println("Tried to read over END_OF_INPUTSTREAM!"); + return null; + } + + // Check for inserted scanner-tokens + if (currentToken instanceof InsertedScannerToken) { + InsertedScannerToken inserted = (InsertedScannerToken) currentToken; + if (inserted.getErrorInformation() != null) { + notifyObserversAbout(inserted.getErrorInformation()); + } + } + return currentToken; + } + + private ITerminalSeq createLookahead(Collection> buffer, final int k) { + final ITerminalSeq seq = new TerminalSeq(); + + final Iterator> it = buffer.iterator(); + while (seq.size() < k && it.hasNext()) { + seq.append(it.next().getSymbol()); + } + + return seq; + } } diff --git a/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/parser/LRParser.java b/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/parser/LRParser.java index 8d96bd911..1d1106df6 100644 --- a/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/parser/LRParser.java +++ b/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/parser/LRParser.java @@ -2,14 +2,6 @@ import static edu.tum.cup2.semantics.SymbolValue.NoValue; -import java.io.IOException; -import java.io.Serializable; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.LinkedList; -import java.util.List; -import java.util.Stack; - import edu.tum.cup2.grammar.AuxiliaryLHS4SemanticShiftAction; import edu.tum.cup2.grammar.Production; import edu.tum.cup2.grammar.SpecialTerminals; @@ -35,812 +27,729 @@ import edu.tum.cup2.semantics.Action; import edu.tum.cup2.semantics.ActionPerformer; import edu.tum.cup2.semantics.ErrorInformation; - +import java.io.IOException; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; +import java.util.Objects; +import java.util.Stack; /** * Implementation of an LR parser. - * - * It is initialized with a {@link LRParsingTable} which is used over the whole - * lifetime of this parser. - * - * When a {@link Scanner} is given, the input is parsed accordingly. - * + * + *

It is initialized with a {@link LRParsingTable} which is used over the whole lifetime of this + * parser. + * + *

When a {@link Scanner} is given, the input is parsed accordingly. + * * @author Andreas Wenger * @author Stefan Dangl (SD) */ +public class LRParser extends AParser implements Serializable { + private static final long serialVersionUID = 1L; -public class LRParser extends AParser implements Serializable -{ - private static final long serialVersionUID = 1L; - - private final boolean DEBUG = false; // TODO: debug messages on the terminal - private int maxErrors = Integer.MIN_VALUE; - - // big fat table - protected LRParsingTable table; - - // stacks - private Stack stack; - private Stack valueStack; - private Stack tokenCountStack; - - // list of parsed tokens (backtracking,error-recovery) - private boolean saveTokens; - private List> parsedTokens; - - // stuff for error-recovery - private int lastError_sync_size; - private int lastError_start_sync_size; - private Stack lastError_stateStack = null; - private Stack lastError_tokenCountStack = null; - private List dryRun_savedActions = null; - ErrorInformation lastErrorInformation = null; - - // other things - Scanner input = null; - ScannerToken currentToken; - LRParserState currentState; - - - /** - * Creates a new {@link LRParser}, using the given {@link LRParsingTable}. - */ - public LRParser(LRParsingTable table) - { - this.table = table; - } - - -/** - * Parses the stream of symbols that can be read from the given - * {@link Scanner}. A single parser-instance may not be used for - * concurrent parsing! If used so, the second call to {@link #parse) - * returns null. - * - * @return - * If the start-production has an action returning a value, - * it will be returned. Otherwise null. - * - * @throws MissingErrorRecoveryException - * If an error was encountered in the input and error-recovery - * failed due to missing error-recovery productions. - * @throws EndOfInputstreamException - * If the input ended too early, e.g. if the - * {@link edu.tum.cup2.scanner.Scanner Scanner} returned - * the special token EndOfInputStream in the middle of a - * production. - * @throws IOException - * Re-thrown in case of the scanner throwing IOException on - * {@link edu.tum.cup2.scanner.Scanner#readNextTerminal() - * readNextTerminal()}. - * @throws ErrorStateException - * In case of the critical error of the parser (in detail the - * goto-table) not being compliant to the productions (in detail the - * left-hand-side). - * @throws ErrorActionException - * In case of the critical error of a non-compliant action-table. - */ - - public synchronized Object parse(Scanner input, Object... initArgs) throws LRParserException, IOException - { - return parse(input, false, initArgs); - } - - - public synchronized Object parse(Scanner input, boolean saveTokens, Object... initArgs) throws LRParserException, - IOException - { - try - { - if (this.input != null) - return null; - - // initialization - this.input = input; - this.saveTokens = saveTokens; - table.getParserInterface().init(this, initArgs); - - // create stack and initialize it with start state - stack = new Stack(); - stack.push(table.getStartState()); - - // create stack for the semantic values - valueStack = new Stack(); - valueStack.push(NoValue); - - if (saveTokens) - { - // create stack indicating number of tokens - tokenCountStack = new Stack(); - tokenCountStack.push(new Integer(0)); - - // create list of parsed tokens - parsedTokens = new ArrayList>(); - } - - // initialize variables for error-recovery (mainly dry run) - lastError_sync_size = 0; - lastError_start_sync_size = 0; - lastError_stateStack = null; - lastError_tokenCountStack = null; - dryRun_savedActions = null; - - // read all symbols - currentToken = readNextToken(); - while (true) - { - currentState = stack.peek(); - // look up an action in the action table - LRAction action = table.getActionTable().get(currentState, currentToken.getSymbol()); - - // if consecutive non-associative terminals are encountered, - // we yield a compiler error! - if (action instanceof ConsecutiveNonAssocAction){ - throw new ConsecutiveNonAssocException(currentToken); - } - - // if error-action (i.e. unexpected terminal) encountered, - // try reducing first - but only if only one single reduction is possible! - if (action instanceof ErrorAction) - { - LRAction reduction = searchSingleReduction(); - if (reduction != null) - { - if (DEBUG) - System.out.println("Trying to reduce before recovering error!"); - action = reduction; - } - } - - // ACTION : SHIFT - - if (action instanceof Shift) - { - // shift action - Shift shift = (Shift) action; - if (DEBUG) - System.out.println("S: to state " + shift.getState().getID() + " (" + currentToken.getSymbol() + ")"); // TEST - // push the given state onto the stack, i.e. it becomes the current - // state - LRParserState s = shift.getState(); - if (currentToken.getLine() != -1 || currentToken.getColumn() != -1) - { - s = (LRParserState) (shift.getState().clone()); - s.beginLine = currentToken.getLine(); - s.beginColumn = currentToken.getColumn(); - } - stack.push(s); - - if (parsedTokens != null) - { - // save read symbol as single item in a list on top of the tokenStack - parsedTokens.add(currentToken); - if (saveTokens) - tokenCountStack.push(1); - // System.out.println("saving "+currentToken); - } - - /** - * if the parser is currently in error state, i.e. dry run - * - indicated by lastError_sync_size > 0 - do not perform - * semantic actions, but save them and execute - * them if and when dry run completes correctly. - **/ - if (lastError_sync_size > 0) - { - dryRun_savedActions.add(action); - lastError_sync_size--; - if (lastError_sync_size == 0) - { - if (DEBUG) - System.out.println("DRY RUN COMPLETE - now performing actions on valueStack .."); - dryRun_doReturnToNormal(); - } - } else - { - // put semantic value of the symbol onto the stack (or NoValue if it has none) - valueStack.push(currentToken.hasValue() ? currentToken.getValue() : NoValue); - } - // read next symbol from input stream - currentToken = readNextToken(); - - - // ACTION : REDUCE - - } else if (action instanceof Reduce) - { - // reduce action - Reduce reduce = (Reduce) action; - Production production = reduce.getProduction(); - int numTokensPopped = 0; - - if (DEBUG) - System.out.println("R: " + production.toString() + " (" + currentToken.getSymbol() + ")"); // TEST - - LRParserState lastBeginInfo = stack.peek(); - - /** - * if the parser is currently in error state, i.e. dry run - * - indicated by lastError_sync_size > 0 - do not perform - * semantic actions, but save them and execute - * them if and when dry run completes correctly. - **/ - if (lastError_sync_size > 0) - { - dryRun_savedActions.add(action); - // for each symbol in the right-hand side of rule, remove one state from - // the stack and NOT from the value stack - for (int i = 0; i < production.getRHSSizeWithoutEpsilon(); i++) - { - lastBeginInfo = stack.pop(); - if (saveTokens) - numTokensPopped += tokenCountStack.pop(); - } - } else - { - // perform the assigned semantic action - Action reduceAction = reduce.getAction(); - Object newValue = NoValue; - ; - if (reduceAction != null) - { - newValue = ActionPerformer - .perform( - reduceAction, - valueStack, - production.getRHSSizeWithoutEpsilon() - + ((production.getLHS() instanceof AuxiliaryLHS4SemanticShiftAction) ? ((AuxiliaryLHS4SemanticShiftAction) production - .getLHS()).numPrecedingSymbolsNotEpsilon : 0)); - } - // for each symbol in the right-hand side of rule, remove one state from - // the stack and from the value stack - for (int i = 0; i < production.getRHSSizeWithoutEpsilon(); i++) - { - lastBeginInfo = stack.pop(); - valueStack.pop(); - if (saveTokens) - numTokensPopped += tokenCountStack.pop(); - } - // put the semantic value of this production onto the value stack - valueStack.push(newValue); - } - // push a new state onto the stack (i.e. set the current state), - // namely the state that is found in the goto table at the position - // of the current state and the left-hand side of the rule which was - // reduced - LRParserState newState = table.getGotoTable().get(stack.peek(), production.getLHS()); - if (newState instanceof ErrorState) - { - table.getParserInterface().exit(this); - throw new ErrorStateException(stack.peek(), production.getLHS()); - } else - { - LRParserState s = (LRParserState) newState.clone(); - s.beginColumn = lastBeginInfo.beginColumn; - s.beginLine = lastBeginInfo.beginLine; - stack.push(s); - if (DEBUG) - System.out.println("G: to state " + newState.getID() + " (" + currentToken.getSymbol() + ")"); // TEST - } - // put the number of tokens used for this production onto the tokenStack - if (saveTokens) - tokenCountStack.push(numTokensPopped); - - - // ACTION : ACCEPT - - } else if (action instanceof Accept) - { - /* - * // assume here is highest memory-footprint - * System.gc(); - * System.runFinalization(); - * System.gc(); - * System.out.println(Runtime.getRuntime().totalMemory()-Runtime.getRuntime().freeMemory()); - */ - // accept action - if (lastError_sync_size > 0) - { - // DRY RUN ACTIVE ON COMPLETION - if (DEBUG) - System.out.println("FINISHED, BUT DRY RUN STILL ACTIVE - now performing actions on valueStack .."); - parsedTokens.add(currentToken); // parsedTokens != null - lastError_sync_size--; - dryRun_doReturnToNormal(); - } - if (DEBUG) - System.out.println("FINISHED :-)"); - Object item = valueStack.peek(); - // free memory and indicate deinitialization - stack = null; - valueStack = null; - tokenCountStack = null; - this.input = null; - table.getParserInterface().exit(this); - // return item - return item; - - - // ACTION : ERROR - } else if (action instanceof ErrorAction) - { - - // ANDI TEST - System.err.println("LINE: " + currentToken.getLine() + " / COLUMN: " + currentToken.getColumn()); - - if (DEBUG) - System.out.println("ErrorAction found! Starting error-recovery..."); - if (maxErrors > 0 || maxErrors == Integer.MIN_VALUE) - startPhraseBasedErrorRecovery(); - else - throw new LRParserException("Too many errors!"); - - - // ACTION : UNKNOWN - } else - { - // TODO : Must never occur! - if (DEBUG) - { - System.err.println("Critical internal error!\nUnknown action : " + action); - if (action != null) - System.err.println(" class : " + action.getClass().getSimpleName()); - } - table.getParserInterface().exit(this); - throw new ErrorActionException(currentState, currentToken.getSymbol()); - } - } - } catch (Exception e) - { - // free memory - stack = null; - valueStack = null; - tokenCountStack = null; - this.input = null; - - // runtime exceptions may occur in actions - if (e instanceof RuntimeException) - throw (RuntimeException) e; - table.getParserInterface().exit(this); - if (e instanceof LRParserException) - throw (LRParserException) e; - if (e instanceof IOException) - throw (IOException) e; - System.err.println("Internal error : Exception " + e.getClass() + " caught by the parser!"); - return null; // no other exceptions allowed! - } - /** - * unreachable code - * // free memory and inform parser-interface - * stack = null; - * valueStack = null; - * tokenCountStack = null; - * this.input = null; - * table.getParserInterface().exit(this); - **/ - } - - - @SuppressWarnings("unchecked") - private ScannerToken readNextToken() throws IOException - { - ScannerToken cur = input.readNextTerminal(); - /** Safety check **/ - if (cur == null) - throw new IOException("Null-token received! Check your scanner implementation!"); - /** Check for inserted scanner-tokens **/ - if (cur instanceof InsertedScannerToken) - { - InsertedScannerToken inserted = (InsertedScannerToken) cur; - if (inserted.getErrorInformation() != null) - doNotifyObserversAbout(inserted.getErrorInformation()); - } - - // TEST ANDI - // System.out.println(cur.getSymbol()); - - return cur; - } - - - /** - * If in the current state only one single - * reduction is possible, this function returns it. - * Otherwise returns null. - **/ - private LRAction searchSingleReduction() - { - Reduce act = null; - for (Terminal t : table.getActionTable().getColumns()) - { - LRAction act2 = table.getActionTable().get(currentState, t); - if (act2 instanceof Reduce) - { - if (act == null) - act = (Reduce) act2; - else if (((Reduce) act2).getProduction() != act.getProduction()) - return null; - } else if (act2 instanceof Shift) - return null; - } - return act; - } - - - /** - * This method may start dry-run. - * - * @author Stefan Dangl - * - * Phrase-based Error-Recovery - * - * Definition: An error-recovering state or a state which is - * able to perform error-recovery is a state which has an action - * (shift or reduce) associated with the special terminal Error. - * - * While there is a state on top of the stack which is not able - * to perform error-recovery, pop that state. If the stack runs - * empty before any error-recovering state could be found, this - * method has to throw a MissingErrorRecoveryException. - * - * After this procedure, the earliest production which recovers - * from the error has been chosen and must be supplied with an - * appropriate object of type ErrorInformation. It is until now - * unknown to which extent the error must be recovered from. - * Thus, it is required for the algorithm to read as much tokens - * from the input-stream (the scanner) until normal parsing may - * proceed. - * - * The state indicated to by the terminal Error in the action - * table is the next state from which to continue reading - * correctly fitting terminals. Pushing that state on the stack - * early (before it's value has been determined) is possible, - * because parsing is paused. As long as the currently processed - * terminal is not adequate (i.e. no actions for this state and - * current terminal are found in the action-table), it must be - * added to the list of "bad" terminals and the next terminal is - * to be read and checked the same way. This procedure continues - * until a fitting terminal was read or the end of the input - * stream was reached. In the latter case, the error could not - * be recovered from and a suitable exception will be thrown - * (currently EndOfInputstreamException). Otherwise, recovery - * seems to be complete. - * - * A new instance of ErrorInformation - supplied with the - * information about popped values, popped terminals and read - * terminals - is to be pushed onto the stack. - * - * Dry run - * - * To prevent a single error from producing many error messages, - * the parser remains in an error state until it processes - * enough tokens following the error without hitting a new - * error. The number of sufficient tokens must be provided by - * the author of the grammar. Currently, this is a hard-coded value - * (see method error_sync_size() below ). The procedure of - * reading a specific amount of terminals before switching back - * to normal processing-mode is also called "dry run", because - * no semantic action is executed during this simulated - * parsing-procedure. - * - * If another error is found during the dry run, the parser - * merges the two errors as if they occured as one single error - * before starting to recover from that second error (which - * occured in error state and indicates only, that the decision - * about completion of recovery based on one single terminal was - * probably incomplete). The merging of two errors follows a - * simple procedure: All terminals on the stack on top of the - * previously pushed ErrorInformation are popped from the stack - * and added to the end of the list of read terminals of the - * ErrorInformation instance. States have to be popped - * accordingly and are to be dismissed. There must not be any - * changes on the value-stack except for the ErrorInformation - * instance to be popped (and saved), because merging must only - * occur when the parser is currently simulating (dry run). Now - * the stacks are reverted to the point where the first error - * occured and recovery can start again, while adding newly read - * terminals to the saved ErrorInformation-instance, which will - * then be pushed onto the stack. - * - * After completion of the dry run, the error is assumed to be - * recovered from. At this point in time, the parser has to be - * reverted to the position before entering the dry run in order - * to parse the now following correct input which has been - * consumed without executing appropriate production's semantic - * actions. Alternatively, the semantic actions have to be - * executed afterwards and the value-stack updated accordingly. - * In CUP2 error-recovery implementation the later solution has - * been chosen as it seems more straightforward. - * - * @throws EndOfInputstreamException - * @throws MissingErrorRecoveryException - * @throws IOException . - * - **/ - @SuppressWarnings("unchecked") - private void startPhraseBasedErrorRecovery() throws IOException, EndOfInputstreamException, - MissingErrorRecoveryException - { - if (DEBUG) - System.out.println("Recover: PBER"); - - LRParserState handleState; - LinkedList popped_values = new LinkedList(); - LinkedList> popped_tokens = new LinkedList>(); - LinkedList> read_tokens = new LinkedList>(); - List expected_terminals = new ArrayList(); - ScannerToken crash_token; - int beginLine = -2; - int beginColumn = -2; - - // Check if previous error has completely been recovered from (sync) - if (lastError_sync_size > 0) - { - // previous error is still unsynchronized -> combine errors. - if (DEBUG) - System.out.println("Previous error is still unsynchronized (sync:" + lastError_sync_size - + ")! Combining errors..."); - if (saveTokens) - { - // find all terminals read between errors and - // copy them to read_tokens - int parsedTokensOffset = parsedTokens.size() - (lastError_start_sync_size - lastError_sync_size); - for (int i = parsedTokensOffset; i < parsedTokens.size(); i++) - read_tokens.add(parsedTokens.get(i)); - } - // revert stacks - stack = lastError_stateStack; - tokenCountStack = lastError_tokenCountStack; - handleState = stack.pop(); - ErrorInformation oldErrInfo = (ErrorInformation) valueStack.pop(); - beginLine = oldErrInfo.getBeginLine(); - beginColumn = oldErrInfo.getBeginColumn(); - crash_token = oldErrInfo.getCrashToken(); - if (!saveTokens) - { - tokenCountStack = null; - parsedTokens = null; - } else - tokenCountStack.pop(); - // adapt information for new ErrorInformation object - for (Object x : oldErrInfo.getPoppedValues()) - popped_values.add(x); - if (saveTokens) - { - for (ScannerToken x : oldErrInfo.getCorrectTokens()) - popped_tokens.add(x); - for (int i = oldErrInfo.getBadTokens().length - 1; i >= 0; i--) - read_tokens.addFirst(oldErrInfo.getBadTokens()[i]); - } - expected_terminals = Arrays.asList(oldErrInfo.getExpectedTerminals()); - // LR(0) - if (lastError_sync_size == lastError_start_sync_size) - { - // nothing changed => read one token (LR(0)) - if (currentToken.getSymbol() != SpecialTerminals.EndOfInputStream) - { - read_tokens.add(currentToken); - } - currentToken = readNextToken(); - } - } else - { // no previous unsynchronized error - crash_token = currentToken; - // This is a new error! - // Create list of expected terminals - for (Terminal t : table.getActionTable().getColumns()) - { - if (t == SpecialTerminals.Error) - continue; - if (table.getActionTable().getWithNull(currentState, t) != null) - expected_terminals.add(t); - } - // Search for state which can shift an error -> Reduces stack - LRAction handleAction; - try - { - LRParserState catchState = stack.peek(); - handleAction = table.getActionTable().get(catchState, SpecialTerminals.Error); - int parsedTokensOffset = 0; - beginColumn = currentToken.getColumn(); - beginLine = currentToken.getLine(); - while (!(handleAction instanceof Shift)) - { - beginColumn = catchState.beginColumn; - beginLine = catchState.beginLine; - @SuppressWarnings("unused") - LRParserState popped_state = stack.pop(); - Object pop = valueStack.pop(); - if (pop == null || !pop.equals(NoValue)) - popped_values.addFirst(pop); - if (saveTokens) - { - int cnt = tokenCountStack.pop(); - int end = parsedTokens.size() + parsedTokensOffset; - for (int i = end - 1; i >= end - cnt; i--) - popped_tokens.addFirst(parsedTokens.get(i)); - parsedTokensOffset -= cnt; - } - catchState = stack.peek(); - handleAction = table.getActionTable().get(catchState, SpecialTerminals.Error); - } - if (DEBUG) - { - System.out.println("Found error-catching state : " + catchState); - System.out.println(" with popped values : " + popped_values); - System.out.println(" and popped tokens : " + popped_tokens); - } - } catch (java.util.EmptyStackException e) - { - // notify observers about non-recoverable error. - ErrorInformation errInf = new ErrorInformation(crash_token, false, popped_values.toArray(), - popped_tokens.toArray(new ScannerToken[] {}), read_tokens.toArray(new ScannerToken[] {}), - expected_terminals.toArray(new Terminal[] {}), crash_token.getLine(), crash_token.getColumn(), - crash_token.getLine(), crash_token.getColumn()); - doNotifyObserversAbout(errInf); - // throw exception. - if (currentToken.getSymbol() == SpecialTerminals.EndOfInputStream) - { - if (DEBUG) - System.out.println("End of input during error-recovery :("); - throw new EndOfInputstreamException("Input does not match grammar, recovery was not possible.", - currentState, currentToken); - } - if (DEBUG) - System.out.println("No production for phrase-based error-recovery found :("); - throw new MissingErrorRecoveryException( - "Input does not match grammar. Grammar does not provide error-correction for current parsing.", - currentState, currentToken, errInf); - } - - handleState = ((Shift) handleAction).getState(); - if (DEBUG) - System.out.println(" Error-shifting to state : " + handleState); - } - - // Search for terminal which let's us proceed. - while (table.getActionTable().get(handleState, currentToken.getSymbol()) instanceof ErrorAction - || currentToken.getSymbol() == SpecialTerminals.EndOfInputStream) - { - if (currentToken.getSymbol() == SpecialTerminals.EndOfInputStream) - { - // notify observers about unrecovered error. - doNotifyObserversAbout(new ErrorInformation(crash_token, false, popped_values.toArray(), - popped_tokens.toArray(new ScannerToken[] {}), read_tokens.toArray(new ScannerToken[] {}), - expected_terminals.toArray(new Terminal[] {}), beginLine, beginColumn, currentToken.getLine(), - currentToken.getColumn())); - // throw useful exception - if (DEBUG) - System.out.println("End of input during error-recovery :("); - throw new EndOfInputstreamException("Input does not match grammar, recovery was not possible.", - handleState, currentToken); - } - if (saveTokens) - read_tokens.addLast(currentToken); - currentToken = readNextToken(); - } - - // Push the correct state and the error-information object on the - // value-stack - LRParserState s = (LRParserState) handleState.clone(); - s.beginColumn = beginColumn; - s.beginLine = beginLine; - stack.push(s); - lastErrorInformation = new ErrorInformation(crash_token, true, popped_values.toArray(), - popped_tokens.toArray(new ScannerToken[] {}), read_tokens.toArray(new ScannerToken[] {}), - expected_terminals.toArray(new Terminal[] {}), beginLine, beginColumn, currentToken.getLine(), - currentToken.getColumn()); - valueStack.push(lastErrorInformation); - if (saveTokens) - tokenCountStack.push(lastErrorInformation.getTokens().length); - - // Initialize values for error-synchronization (dry run) - lastError_stateStack = (Stack) stack.clone(); - lastError_sync_size = table.getParserInterface().getErrorSyncSize(); - if (lastError_sync_size < 0) - lastError_sync_size = 0; - if (saveTokens) - lastError_tokenCountStack = (Stack) tokenCountStack.clone(); - else if (lastError_sync_size != 0) - parsedTokens = new ArrayList>(); - - lastError_start_sync_size = lastError_sync_size; - dryRun_savedActions = new ArrayList(); - if (DEBUG) - System.out.println("Switching to dry-run mode for " + lastError_sync_size + " tokens."); - } - - - /** This method performs everything to return from dry-run to normal mode **/ - private void dryRun_doReturnToNormal() - { - if (maxErrors > 0) - maxErrors--; - // Create list of tokens which have been read during dry run. - int parsedTokensOffset = parsedTokens.size() - (lastError_start_sync_size - lastError_sync_size); - // Perform saved semantic actions - int tokenIndex = 0; - doNotifyObserversAbout(lastErrorInformation); - lastErrorInformation = null; - for (LRAction action2 : dryRun_savedActions) - { - if (action2 instanceof Shift) - { - Shift shift2 = (Shift) action2; - if (DEBUG) - System.out.println("DryRunReturn2Normal S: to state " + shift2.getState().getID() + " (" - + parsedTokens.get(parsedTokensOffset + tokenIndex).getSymbol() + ")"); // TEST - // put semantic value of the symbol onto the stack (or NoValue if it has - // none) - valueStack.push(parsedTokens.get(parsedTokensOffset + tokenIndex).hasValue() ? parsedTokens.get( - parsedTokensOffset + tokenIndex).getValue() : NoValue); - // "read" next token - tokenIndex++; - } else if (action2 instanceof Reduce) - { - Reduce reduce = (Reduce) action2; - if (DEBUG) - System.out.println("DryRunReturn2Normal R: " + reduce.getProduction().toString() + " (" - + parsedTokens.get(parsedTokensOffset + tokenIndex).getSymbol() + ")"); // TEST - // perform the assigned semantic action - Action reduceAction = reduce.getAction(); - Object newValue = NoValue; - if (reduceAction != null) - { - newValue = ActionPerformer - .perform( - reduceAction, - valueStack, - reduce.getProduction().getRHSSizeWithoutEpsilon() - + ((reduce.getProduction().getLHS() instanceof AuxiliaryLHS4SemanticShiftAction) ? ((AuxiliaryLHS4SemanticShiftAction) reduce - .getProduction().getLHS()).numPrecedingSymbolsNotEpsilon : 0)); - } - // for each symbol in the right-hand side of rule, remove one - // item from the value stack - for (int i = 0; i < reduce.getProduction().getRHSSizeWithoutEpsilon(); i++) - { - valueStack.pop(); - } - // put the semantic value of this production onto the value stack - valueStack.push(newValue); - } else - { - // MUST NEVER OCCUR! CAN NOT OCCUR! IS EVIL! - throw new RuntimeException( - "Critical internal error in parser driver : Found illegal action while returning to normal mode from dry run!"); - } - } - // kill data-structures required for incomplete dry run. - if (!saveTokens) - { - parsedTokens = null; - tokenCountStack = null; - } - lastError_stateStack = null; - lastError_tokenCountStack = null; - dryRun_savedActions = null; - // reset to normal mode - lastError_sync_size = 0; - lastError_start_sync_size = 0; - } - - - /* - * Methods called from outside - * ( mainly from inside the current specification / - * the current ParserInterface ) - */ - - public boolean initialized() - { - return stack != null; - } - - - private void doNotifyObserversAbout(ErrorInformation e) - { - table.getParserInterface().error(e); - notifyObserversAbout(e); - } - + private final boolean DEBUG = false; // TODO: debug messages on the terminal + private int maxErrors = Integer.MIN_VALUE; + + // big fat table + protected LRParsingTable table; + + // stacks + private Stack stack; + private Stack valueStack; + private Stack tokenCountStack; + + // list of parsed tokens (backtracking,error-recovery) + private boolean saveTokens; + private List> parsedTokens; + + // stuff for error-recovery + private int lastError_sync_size; + private int lastError_start_sync_size; + private Stack lastError_stateStack = null; + private Stack lastError_tokenCountStack = null; + private List dryRun_savedActions = null; + ErrorInformation lastErrorInformation = null; + + // other things + Scanner input = null; + ScannerToken currentToken; + LRParserState currentState; + + /** Creates a new {@link LRParser}, using the given {@link LRParsingTable}. */ + public LRParser(LRParsingTable table) { + this.table = table; + } + + /** + * Parses the stream of symbols that can be read from the given + * {@link Scanner}. A single parser-instance may not be used for + * concurrent parsing! If used so, the second call to {@link #parse) + * returns null. + * + * @return + * If the start-production has an action returning a value, + * it will be returned. Otherwise null. + * + * @throws MissingErrorRecoveryException + * If an error was encountered in the input and error-recovery + * failed due to missing error-recovery productions. + * @throws EndOfInputstreamException + * If the input ended too early, e.g. if the + * {@link edu.tum.cup2.scanner.Scanner Scanner} returned + * the special token EndOfInputStream in the middle of a + * production. + * @throws IOException + * Re-thrown in case of the scanner throwing IOException on + * {@link edu.tum.cup2.scanner.Scanner#readNextTerminal() + * readNextTerminal()}. + * @throws ErrorStateException + * In case of the critical error of the parser (in detail the + * goto-table) not being compliant to the productions (in detail the + * left-hand-side). + * @throws ErrorActionException + * In case of the critical error of a non-compliant action-table. + */ + public synchronized Object parse(Scanner input, Object... initArgs) + throws LRParserException, IOException { + return parse(input, false, initArgs); + } + + public synchronized Object parse(Scanner input, boolean saveTokens, Object... initArgs) + throws LRParserException, IOException { + try { + if (this.input != null) return null; + + // initialization + this.input = input; + this.saveTokens = saveTokens; + table.getParserInterface().init(this, initArgs); + + // create stack and initialize it with start state + stack = new Stack(); + stack.push(table.getStartState()); + + // create stack for the semantic values + valueStack = new Stack(); + valueStack.push(NoValue); + + if (saveTokens) { + // create stack indicating number of tokens + tokenCountStack = new Stack(); + tokenCountStack.push(new Integer(0)); + + // create list of parsed tokens + parsedTokens = new ArrayList>(); + } + + // initialize variables for error-recovery (mainly dry run) + lastError_sync_size = 0; + lastError_start_sync_size = 0; + lastError_stateStack = null; + lastError_tokenCountStack = null; + dryRun_savedActions = null; + + // read all symbols + currentToken = readNextToken(); + while (true) { + currentState = stack.peek(); + // look up an action in the action table + LRAction action = table.getActionTable().get(currentState, currentToken.getSymbol()); + + // if consecutive non-associative terminals are encountered, + // we yield a compiler error! + if (action instanceof ConsecutiveNonAssocAction) { + throw new ConsecutiveNonAssocException(currentToken); + } + + // if error-action (i.e. unexpected terminal) encountered, + // try reducing first - but only if only one single reduction is possible! + if (action instanceof ErrorAction) { + LRAction reduction = searchSingleReduction(); + if (reduction != null) { + if (DEBUG) System.out.println("Trying to reduce before recovering error!"); + action = reduction; + } + } + + // ACTION : SHIFT + + if (action instanceof Shift) { + // shift action + Shift shift = (Shift) action; + if (DEBUG) + System.out.println( + "S: to state " + + shift.getState().getID() + + " (" + + currentToken.getSymbol() + + ")"); // TEST + // push the given state onto the stack, i.e. it becomes the current + // state + LRParserState s = shift.getState(); + if (currentToken.getLine() != -1 || currentToken.getColumn() != -1) { + s = (LRParserState) (shift.getState().clone()); + s.beginLine = currentToken.getLine(); + s.beginColumn = currentToken.getColumn(); + } + stack.push(s); + + if (parsedTokens != null) { + // save read symbol as single item in a list on top of the tokenStack + parsedTokens.add(currentToken); + if (saveTokens) tokenCountStack.push(1); + // System.out.println("saving "+currentToken); + } + + /** + * if the parser is currently in error state, i.e. dry run - indicated by + * lastError_sync_size > 0 - do not perform semantic actions, but save them and execute + * them if and when dry run completes correctly. + */ + if (lastError_sync_size > 0) { + dryRun_savedActions.add(action); + lastError_sync_size--; + if (lastError_sync_size == 0) { + if (DEBUG) + System.out.println("DRY RUN COMPLETE - now performing actions on valueStack .."); + dryRun_doReturnToNormal(); + } + } else { + // put semantic value of the symbol onto the stack (or NoValue if it has none) + valueStack.push(currentToken.hasValue() ? currentToken.getValue() : NoValue); + } + // read next symbol from input stream + currentToken = readNextToken(); + + // ACTION : REDUCE + + } else if (action instanceof Reduce) { + // reduce action + Reduce reduce = (Reduce) action; + Production production = reduce.getProduction(); + int numTokensPopped = 0; + + if (DEBUG) + System.out.println( + "R: " + production.toString() + " (" + currentToken.getSymbol() + ")"); // TEST + + LRParserState lastBeginInfo = stack.peek(); + + /** + * if the parser is currently in error state, i.e. dry run - indicated by + * lastError_sync_size > 0 - do not perform semantic actions, but save them and execute + * them if and when dry run completes correctly. + */ + if (lastError_sync_size > 0) { + dryRun_savedActions.add(action); + // for each symbol in the right-hand side of rule, remove one state from + // the stack and NOT from the value stack + for (int i = 0; i < production.getRHSSizeWithoutEpsilon(); i++) { + lastBeginInfo = stack.pop(); + if (saveTokens) numTokensPopped += tokenCountStack.pop(); + } + } else { + // perform the assigned semantic action + Action reduceAction = reduce.getAction(); + Object newValue = NoValue; + ; + if (reduceAction != null) { + newValue = + ActionPerformer.perform( + reduceAction, + valueStack, + production.getRHSSizeWithoutEpsilon() + + ((production.getLHS() instanceof AuxiliaryLHS4SemanticShiftAction) + ? ((AuxiliaryLHS4SemanticShiftAction) production.getLHS()) + .numPrecedingSymbolsNotEpsilon + : 0)); + } + // for each symbol in the right-hand side of rule, remove one state from + // the stack and from the value stack + for (int i = 0; i < production.getRHSSizeWithoutEpsilon(); i++) { + lastBeginInfo = stack.pop(); + valueStack.pop(); + if (saveTokens) numTokensPopped += tokenCountStack.pop(); + } + // put the semantic value of this production onto the value stack + valueStack.push(newValue); + } + // push a new state onto the stack (i.e. set the current state), + // namely the state that is found in the goto table at the position + // of the current state and the left-hand side of the rule which was + // reduced + LRParserState newState = table.getGotoTable().get(stack.peek(), production.getLHS()); + if (newState instanceof ErrorState) { + table.getParserInterface().exit(this); + throw new ErrorStateException(stack.peek(), production.getLHS()); + } else { + LRParserState s = (LRParserState) newState.clone(); + s.beginColumn = lastBeginInfo.beginColumn; + s.beginLine = lastBeginInfo.beginLine; + stack.push(s); + if (DEBUG) + System.out.println( + "G: to state " + + newState.getID() + + " (" + + currentToken.getSymbol() + + ")"); // TEST + } + // put the number of tokens used for this production onto the tokenStack + if (saveTokens) tokenCountStack.push(numTokensPopped); + + // ACTION : ACCEPT + + } else if (action instanceof Accept) { + /* + * // assume here is highest memory-footprint + * System.gc(); + * System.runFinalization(); + * System.gc(); + * System.out.println(Runtime.getRuntime().totalMemory()-Runtime.getRuntime().freeMemory()); + */ + // accept action + if (lastError_sync_size > 0) { + // DRY RUN ACTIVE ON COMPLETION + if (DEBUG) + System.out.println( + "FINISHED, BUT DRY RUN STILL ACTIVE - now performing actions on valueStack .."); + parsedTokens.add(currentToken); // parsedTokens != null + lastError_sync_size--; + dryRun_doReturnToNormal(); + } + if (DEBUG) System.out.println("FINISHED :-)"); + Object item = valueStack.peek(); + // free memory and indicate deinitialization + stack = null; + valueStack = null; + tokenCountStack = null; + this.input = null; + table.getParserInterface().exit(this); + // return item + return item; + + // ACTION : ERROR + } else if (action instanceof ErrorAction) { + + // ANDI TEST + System.err.println( + "LINE: " + currentToken.getLine() + " / COLUMN: " + currentToken.getColumn()); + + if (DEBUG) System.out.println("ErrorAction found! Starting error-recovery..."); + if (maxErrors > 0 || maxErrors == Integer.MIN_VALUE) startPhraseBasedErrorRecovery(); + else throw new LRParserException("Too many errors!"); + + // ACTION : UNKNOWN + } else { + // TODO : Must never occur! + if (DEBUG) { + System.err.println("Critical internal error!\nUnknown action : " + action); + if (action != null) + System.err.println(" class : " + action.getClass().getSimpleName()); + } + table.getParserInterface().exit(this); + throw new ErrorActionException(currentState, currentToken.getSymbol()); + } + } + } catch (Exception e) { + // free memory + stack = null; + valueStack = null; + tokenCountStack = null; + this.input = null; + + // runtime exceptions may occur in actions + if (e instanceof RuntimeException) throw (RuntimeException) e; + table.getParserInterface().exit(this); + if (e instanceof LRParserException) throw (LRParserException) e; + if (e instanceof IOException) throw (IOException) e; + System.err.println("Internal error : Exception " + e.getClass() + " caught by the parser!"); + return null; // no other exceptions allowed! + } + /** + * unreachable code // free memory and inform parser-interface stack = null; valueStack = null; + * tokenCountStack = null; this.input = null; table.getParserInterface().exit(this); + */ + } + + @SuppressWarnings("unchecked") + private ScannerToken readNextToken() throws IOException { + ScannerToken cur = input.readNextTerminal(); + /** Safety check * */ + if (cur == null) + throw new IOException("Null-token received! Check your scanner implementation!"); + /** Check for inserted scanner-tokens * */ + if (cur instanceof InsertedScannerToken) { + InsertedScannerToken inserted = (InsertedScannerToken) cur; + if (inserted.getErrorInformation() != null) + doNotifyObserversAbout(inserted.getErrorInformation()); + } + + // TEST ANDI + // System.out.println(cur.getSymbol()); + + return cur; + } + + /** + * If in the current state only one single reduction is possible, this function returns it. + * Otherwise returns null. + */ + private LRAction searchSingleReduction() { + Reduce act = null; + for (Terminal t : table.getActionTable().getColumns()) { + LRAction act2 = table.getActionTable().get(currentState, t); + if (act2 instanceof Reduce) { + if (act == null) act = (Reduce) act2; + else if (((Reduce) act2).getProduction() != act.getProduction()) return null; + } else if (act2 instanceof Shift) return null; + } + return act; + } + + /** + * This method may start dry-run. + * + * @author Stefan Dangl + *

Phrase-based Error-Recovery + *

Definition: An error-recovering state or a state which is able to perform error-recovery + * is a state which has an action (shift or reduce) associated with the special terminal + * Error. + *

While there is a state on top of the stack which is not able to perform error-recovery, + * pop that state. If the stack runs empty before any error-recovering state could be found, + * this method has to throw a MissingErrorRecoveryException. + *

After this procedure, the earliest production which recovers from the error has been + * chosen and must be supplied with an appropriate object of type ErrorInformation. It is + * until now unknown to which extent the error must be recovered from. Thus, it is required + * for the algorithm to read as much tokens from the input-stream (the scanner) until normal + * parsing may proceed. + *

The state indicated to by the terminal Error in the action table is the next state from + * which to continue reading correctly fitting terminals. Pushing that state on the stack + * early (before it's value has been determined) is possible, because parsing is paused. As + * long as the currently processed terminal is not adequate (i.e. no actions for this state + * and current terminal are found in the action-table), it must be added to the list of "bad" + * terminals and the next terminal is to be read and checked the same way. This procedure + * continues until a fitting terminal was read or the end of the input stream was reached. In + * the latter case, the error could not be recovered from and a suitable exception will be + * thrown (currently EndOfInputstreamException). Otherwise, recovery seems to be complete. + *

A new instance of ErrorInformation - supplied with the information about popped values, + * popped terminals and read terminals - is to be pushed onto the stack. + *

Dry run + *

To prevent a single error from producing many error messages, the parser remains in an + * error state until it processes enough tokens following the error without hitting a new + * error. The number of sufficient tokens must be provided by the author of the grammar. + * Currently, this is a hard-coded value (see method error_sync_size() below ). The procedure + * of reading a specific amount of terminals before switching back to normal processing-mode + * is also called "dry run", because no semantic action is executed during this simulated + * parsing-procedure. + *

If another error is found during the dry run, the parser merges the two errors as if + * they occured as one single error before starting to recover from that second error (which + * occured in error state and indicates only, that the decision about completion of recovery + * based on one single terminal was probably incomplete). The merging of two errors follows a + * simple procedure: All terminals on the stack on top of the previously pushed + * ErrorInformation are popped from the stack and added to the end of the list of read + * terminals of the ErrorInformation instance. States have to be popped accordingly and are to + * be dismissed. There must not be any changes on the value-stack except for the + * ErrorInformation instance to be popped (and saved), because merging must only occur when + * the parser is currently simulating (dry run). Now the stacks are reverted to the point + * where the first error occured and recovery can start again, while adding newly read + * terminals to the saved ErrorInformation-instance, which will then be pushed onto the stack. + *

After completion of the dry run, the error is assumed to be recovered from. At this + * point in time, the parser has to be reverted to the position before entering the dry run in + * order to parse the now following correct input which has been consumed without executing + * appropriate production's semantic actions. Alternatively, the semantic actions have to be + * executed afterwards and the value-stack updated accordingly. In CUP2 error-recovery + * implementation the later solution has been chosen as it seems more straightforward. + * @throws EndOfInputstreamException + * @throws MissingErrorRecoveryException + * @throws IOException . + */ + @SuppressWarnings("unchecked") + private void startPhraseBasedErrorRecovery() + throws IOException, EndOfInputstreamException, MissingErrorRecoveryException { + if (DEBUG) System.out.println("Recover: PBER"); + + LRParserState handleState; + LinkedList popped_values = new LinkedList(); + LinkedList> popped_tokens = + new LinkedList>(); + LinkedList> read_tokens = + new LinkedList>(); + List expected_terminals = new ArrayList(); + ScannerToken crash_token; + int beginLine = -2; + int beginColumn = -2; + + // Check if previous error has completely been recovered from (sync) + if (lastError_sync_size > 0) { + // previous error is still unsynchronized -> combine errors. + if (DEBUG) + System.out.println( + "Previous error is still unsynchronized (sync:" + + lastError_sync_size + + ")! Combining errors..."); + if (saveTokens) { + // find all terminals read between errors and + // copy them to read_tokens + int parsedTokensOffset = + parsedTokens.size() - (lastError_start_sync_size - lastError_sync_size); + for (int i = parsedTokensOffset; i < parsedTokens.size(); i++) + read_tokens.add(parsedTokens.get(i)); + } + // revert stacks + stack = lastError_stateStack; + tokenCountStack = lastError_tokenCountStack; + handleState = stack.pop(); + ErrorInformation oldErrInfo = (ErrorInformation) valueStack.pop(); + beginLine = oldErrInfo.getBeginLine(); + beginColumn = oldErrInfo.getBeginColumn(); + crash_token = oldErrInfo.getCrashToken(); + if (!saveTokens) { + tokenCountStack = null; + parsedTokens = null; + } else tokenCountStack.pop(); + // adapt information for new ErrorInformation object + for (Object x : oldErrInfo.getPoppedValues()) popped_values.add(x); + if (saveTokens) { + for (ScannerToken x : oldErrInfo.getCorrectTokens()) popped_tokens.add(x); + for (int i = oldErrInfo.getBadTokens().length - 1; i >= 0; i--) + read_tokens.addFirst(oldErrInfo.getBadTokens()[i]); + } + expected_terminals = Arrays.asList(oldErrInfo.getExpectedTerminals()); + // LR(0) + if (lastError_sync_size == lastError_start_sync_size) { + // nothing changed => read one token (LR(0)) + if (currentToken.getSymbol() != SpecialTerminals.EndOfInputStream) { + read_tokens.add(currentToken); + } + currentToken = readNextToken(); + } + } else { // no previous unsynchronized error + crash_token = currentToken; + // This is a new error! + // Create list of expected terminals + for (Terminal t : table.getActionTable().getColumns()) { + if (t == SpecialTerminals.Error) continue; + if (table.getActionTable().getWithNull(currentState, t) != null) expected_terminals.add(t); + } + // Search for state which can shift an error -> Reduces stack + LRAction handleAction; + try { + LRParserState catchState = stack.peek(); + handleAction = table.getActionTable().get(catchState, SpecialTerminals.Error); + int parsedTokensOffset = 0; + beginColumn = currentToken.getColumn(); + beginLine = currentToken.getLine(); + while (!(handleAction instanceof Shift)) { + beginColumn = catchState.beginColumn; + beginLine = catchState.beginLine; + @SuppressWarnings("unused") + LRParserState popped_state = stack.pop(); + Object pop = valueStack.pop(); + if (pop == null || !Objects.equals(pop, NoValue)) popped_values.addFirst(pop); + if (saveTokens) { + int cnt = tokenCountStack.pop(); + int end = parsedTokens.size() + parsedTokensOffset; + for (int i = end - 1; i >= end - cnt; i--) popped_tokens.addFirst(parsedTokens.get(i)); + parsedTokensOffset -= cnt; + } + catchState = stack.peek(); + handleAction = table.getActionTable().get(catchState, SpecialTerminals.Error); + } + if (DEBUG) { + System.out.println("Found error-catching state : " + catchState); + System.out.println(" with popped values : " + popped_values); + System.out.println(" and popped tokens : " + popped_tokens); + } + } catch (java.util.EmptyStackException e) { + // notify observers about non-recoverable error. + ErrorInformation errInf = + new ErrorInformation( + crash_token, + false, + popped_values.toArray(), + popped_tokens.toArray(new ScannerToken[] {}), + read_tokens.toArray(new ScannerToken[] {}), + expected_terminals.toArray(new Terminal[] {}), + crash_token.getLine(), + crash_token.getColumn(), + crash_token.getLine(), + crash_token.getColumn()); + doNotifyObserversAbout(errInf); + // throw exception. + if (currentToken.getSymbol() == SpecialTerminals.EndOfInputStream) { + if (DEBUG) System.out.println("End of input during error-recovery :("); + throw new EndOfInputstreamException( + "Input does not match grammar, recovery was not possible.", + currentState, + currentToken); + } + if (DEBUG) System.out.println("No production for phrase-based error-recovery found :("); + throw new MissingErrorRecoveryException( + "Input does not match grammar. Grammar does not provide error-correction for current parsing.", + currentState, + currentToken, + errInf); + } + + handleState = ((Shift) handleAction).getState(); + if (DEBUG) System.out.println(" Error-shifting to state : " + handleState); + } + + // Search for terminal which let's us proceed. + while (table.getActionTable().get(handleState, currentToken.getSymbol()) instanceof ErrorAction + || currentToken.getSymbol() == SpecialTerminals.EndOfInputStream) { + if (currentToken.getSymbol() == SpecialTerminals.EndOfInputStream) { + // notify observers about unrecovered error. + doNotifyObserversAbout( + new ErrorInformation( + crash_token, + false, + popped_values.toArray(), + popped_tokens.toArray(new ScannerToken[] {}), + read_tokens.toArray(new ScannerToken[] {}), + expected_terminals.toArray(new Terminal[] {}), + beginLine, + beginColumn, + currentToken.getLine(), + currentToken.getColumn())); + // throw useful exception + if (DEBUG) System.out.println("End of input during error-recovery :("); + throw new EndOfInputstreamException( + "Input does not match grammar, recovery was not possible.", handleState, currentToken); + } + if (saveTokens) read_tokens.addLast(currentToken); + currentToken = readNextToken(); + } + + // Push the correct state and the error-information object on the + // value-stack + LRParserState s = (LRParserState) handleState.clone(); + s.beginColumn = beginColumn; + s.beginLine = beginLine; + stack.push(s); + lastErrorInformation = + new ErrorInformation( + crash_token, + true, + popped_values.toArray(), + popped_tokens.toArray(new ScannerToken[] {}), + read_tokens.toArray(new ScannerToken[] {}), + expected_terminals.toArray(new Terminal[] {}), + beginLine, + beginColumn, + currentToken.getLine(), + currentToken.getColumn()); + valueStack.push(lastErrorInformation); + if (saveTokens) tokenCountStack.push(lastErrorInformation.getTokens().length); + + // Initialize values for error-synchronization (dry run) + lastError_stateStack = (Stack) stack.clone(); + lastError_sync_size = table.getParserInterface().getErrorSyncSize(); + if (lastError_sync_size < 0) lastError_sync_size = 0; + if (saveTokens) lastError_tokenCountStack = (Stack) tokenCountStack.clone(); + else if (lastError_sync_size != 0) + parsedTokens = new ArrayList>(); + + lastError_start_sync_size = lastError_sync_size; + dryRun_savedActions = new ArrayList(); + if (DEBUG) + System.out.println("Switching to dry-run mode for " + lastError_sync_size + " tokens."); + } + + /** This method performs everything to return from dry-run to normal mode * */ + private void dryRun_doReturnToNormal() { + if (maxErrors > 0) maxErrors--; + // Create list of tokens which have been read during dry run. + int parsedTokensOffset = + parsedTokens.size() - (lastError_start_sync_size - lastError_sync_size); + // Perform saved semantic actions + int tokenIndex = 0; + doNotifyObserversAbout(lastErrorInformation); + lastErrorInformation = null; + for (LRAction action2 : dryRun_savedActions) { + if (action2 instanceof Shift) { + Shift shift2 = (Shift) action2; + if (DEBUG) + System.out.println( + "DryRunReturn2Normal S: to state " + + shift2.getState().getID() + + " (" + + parsedTokens.get(parsedTokensOffset + tokenIndex).getSymbol() + + ")"); // TEST + // put semantic value of the symbol onto the stack (or NoValue if it has + // none) + valueStack.push( + parsedTokens.get(parsedTokensOffset + tokenIndex).hasValue() + ? parsedTokens.get(parsedTokensOffset + tokenIndex).getValue() + : NoValue); + // "read" next token + tokenIndex++; + } else if (action2 instanceof Reduce) { + Reduce reduce = (Reduce) action2; + if (DEBUG) + System.out.println( + "DryRunReturn2Normal R: " + + reduce.getProduction().toString() + + " (" + + parsedTokens.get(parsedTokensOffset + tokenIndex).getSymbol() + + ")"); // TEST + // perform the assigned semantic action + Action reduceAction = reduce.getAction(); + Object newValue = NoValue; + if (reduceAction != null) { + newValue = + ActionPerformer.perform( + reduceAction, + valueStack, + reduce.getProduction().getRHSSizeWithoutEpsilon() + + ((reduce.getProduction().getLHS() + instanceof AuxiliaryLHS4SemanticShiftAction) + ? ((AuxiliaryLHS4SemanticShiftAction) reduce.getProduction().getLHS()) + .numPrecedingSymbolsNotEpsilon + : 0)); + } + // for each symbol in the right-hand side of rule, remove one + // item from the value stack + for (int i = 0; i < reduce.getProduction().getRHSSizeWithoutEpsilon(); i++) { + valueStack.pop(); + } + // put the semantic value of this production onto the value stack + valueStack.push(newValue); + } else { + // MUST NEVER OCCUR! CAN NOT OCCUR! IS EVIL! + throw new RuntimeException( + "Critical internal error in parser driver : Found illegal action while returning to normal mode from dry run!"); + } + } + // kill data-structures required for incomplete dry run. + if (!saveTokens) { + parsedTokens = null; + tokenCountStack = null; + } + lastError_stateStack = null; + lastError_tokenCountStack = null; + dryRun_savedActions = null; + // reset to normal mode + lastError_sync_size = 0; + lastError_start_sync_size = 0; + } + + /* + * Methods called from outside + * ( mainly from inside the current specification / + * the current ParserInterface ) + */ + + public boolean initialized() { + return stack != null; + } + + private void doNotifyObserversAbout(ErrorInformation e) { + table.getParserInterface().error(e); + notifyObserversAbout(e); + } } diff --git a/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/parser/actions/Reduce.java b/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/parser/actions/Reduce.java index 3a5fd004d..6e5bd18ff 100644 --- a/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/parser/actions/Reduce.java +++ b/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/parser/actions/Reduce.java @@ -3,58 +3,43 @@ import edu.tum.cup2.grammar.Production; import edu.tum.cup2.semantics.Action; import java.io.Serializable; +import java.util.Objects; /** * LR reduce parser action. - * + * * @author Andreas Wenger */ -public final class Reduce - implements LRAction, Serializable -{ - private static final long serialVersionUID = 1L; - - private final Production production; - - - public Reduce(Production production) - { - this.production = production; - } - - - public Production getProduction() - { - return production; - } - - - /** - * Gets the semantic action assigned to this reduce action, or null. - */ - public Action getAction() - { - return production.getReduceAction(); - } - - - @Override public boolean equals(Object obj) - { - if (obj instanceof Reduce) - { - Reduce a = (Reduce) obj; - return this.production.equals(a.production); - } - return false; - } - - - @Override public String toString() - { - if (production.getID() > -1) - return "r" + production.getID(); //r - else - return "r(#" + production.getRHS().size() + ")"; //r(#) - } +public final class Reduce implements LRAction, Serializable { + private static final long serialVersionUID = 1L; + private final Production production; + + public Reduce(Production production) { + this.production = production; + } + + public Production getProduction() { + return production; + } + + /** Gets the semantic action assigned to this reduce action, or null. */ + public Action getAction() { + return production.getReduceAction(); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof Reduce) { + Reduce a = (Reduce) obj; + return Objects.equals(this.production, a.production); + } + return false; + } + + @Override + public String toString() { + if (production.getID() > -1) return "r" + production.getID(); // r + else return "r(#" + production.getRHS().size() + ")"; // r(#) + } } diff --git a/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/parser/actions/Shift.java b/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/parser/actions/Shift.java index d75b2848c..55f6b58aa 100644 --- a/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/parser/actions/Shift.java +++ b/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/parser/actions/Shift.java @@ -1,65 +1,51 @@ package edu.tum.cup2.parser.actions; -import java.io.Serializable; - import edu.tum.cup2.grammar.Production; import edu.tum.cup2.parser.states.LRParserState; +import java.io.Serializable; +import java.util.Objects; /** * LR shift parser action. - * + * * @author Andreas Wenger */ -public final class Shift - implements LRAction, Serializable -{ - private static final long serialVersionUID = 1L; - - private final LRParserState state; - private final Production production; - private final int position; +public final class Shift implements LRAction, Serializable { + private static final long serialVersionUID = 1L; + + private final LRParserState state; + private final Production production; + private final int position; + + public Shift(LRParserState state, Production production, int position) { + this.state = state; + this.production = production; + this.position = position; + } + + public LRParserState getState() { + return state; + } + + public Production getProduction() { + return production; + } + + public int getPosition() { + return position; + } - - public Shift(LRParserState state, Production production, int position) - { - this.state = state; - this.production = production; - this.position = position; - } - - - public LRParserState getState() - { - return state; - } - - - public Production getProduction() - { - return production; - } - - - public int getPosition() - { - return position; - } - - @Override public boolean equals(Object obj) - { - if (obj instanceof Shift) - { - Shift a = (Shift) obj; - return this.state.equals(a.state); - } - return false; - } - - - @Override public String toString() - { - return "s" + state.getID(); - } - + @Override + public boolean equals(Object obj) { + if (obj instanceof Shift) { + Shift a = (Shift) obj; + return Objects.equals(this.state, a.state); + } + return false; + } + @Override + public String toString() { + return "s" + state.getID(); + } } diff --git a/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/parser/tables/StateSymbolKey.java b/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/parser/tables/StateSymbolKey.java index 531b7b56e..6e65d24ea 100644 --- a/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/parser/tables/StateSymbolKey.java +++ b/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/parser/tables/StateSymbolKey.java @@ -3,19 +3,18 @@ import edu.tum.cup2.grammar.Symbol; import edu.tum.cup2.parser.states.LRParserState; import java.io.Serializable; +import java.util.Objects; /** - * Helper class to create a one-dimensional key - * from a two-dimensional table index - * ({@link LRParserState} and a {@link Symbol}). - * - * The hash code is always the same for the same - * state and symbol given, so this class can serve + * Helper class to create a one-dimensional key from a two-dimensional table index ({@link + * LRParserState} and a {@link Symbol}). + * + *

The hash code is always the same for the same state and symbol given, so this class can serve * as a hash table key. - * - * This design was chosen to avoid a big tow-dimensional table - * and use a hashtable instead. The mapping is done as showed here: - * + * + *

This design was chosen to avoid a big tow-dimensional table and use a hashtable instead. The + * mapping is done as showed here: + * *

  *        | a | b | c | ... (Terminals)
  * -------------------------------
@@ -26,73 +25,56 @@
  * state3 |   |   |   |
  * -------------------------------
  * 
- * - * For example, the StateSymbolKey for the action - * x would be <state2, b>. - * + * + * For example, the StateSymbolKey for the action x would be + * <state2, b>. + * * @author Andreas Wenger */ -public final class StateSymbolKey implements Serializable -{ - private static final long serialVersionUID = 1L; - - private final LRParserState state; - private final Symbol symbol; - - private final int hashCode; - - - /** - * Creates a key for the given state and symbol. - */ - public StateSymbolKey(LRParserState state, Symbol symbol) - { - //save state and symbol - this.state = state; - this.symbol = symbol; - //compute hash code - int stateNo = state.getID(); - int symbolNo = 0; - if (Enum.class.isInstance(symbol)) - { - //symbols should be enums in most (if not any) cases, but if not, - //hashing works anyway (just not as good). - symbolNo = ((Enum)symbol).ordinal(); - } - hashCode = symbolNo * 1000000 + stateNo; //TODO: find good hash code - } - - - public Symbol getSymbol() - { - return symbol; - } - - - /** - * Compares this object to the specified object. - * The result is {@code true} if and only if the argument is not - * {@code null} and is an {@link StateSymbolKey} object that - * contains the same state and symbol as this object. - */ - public boolean equals(Object obj) - { - if (obj instanceof StateSymbolKey) - { - StateSymbolKey s = (StateSymbolKey) obj; - //TODO: or equals? i think == should be ok, since we're working with unique instances - return state.equals(s.state) && symbol.equals(s.symbol); - } - return false; +public final class StateSymbolKey implements Serializable { + private static final long serialVersionUID = 1L; + + private final LRParserState state; + private final Symbol symbol; + + private final int hashCode; + + /** Creates a key for the given state and symbol. */ + public StateSymbolKey(LRParserState state, Symbol symbol) { + // save state and symbol + this.state = state; + this.symbol = symbol; + // compute hash code + int stateNo = state.getID(); + int symbolNo = 0; + if (Enum.class.isInstance(symbol)) { + // symbols should be enums in most (if not any) cases, but if not, + // hashing works anyway (just not as good). + symbolNo = ((Enum) symbol).ordinal(); + } + hashCode = symbolNo * 1000000 + stateNo; // TODO: find good hash code } - - - /** - * Returns the hash code. + + public Symbol getSymbol() { + return symbol; + } + + /** + * Compares this object to the specified object. The result is {@code true} if and only if the + * argument is not {@code null} and is an {@link StateSymbolKey} object that contains the same + * state and symbol as this object. */ - public int hashCode() - { - return hashCode; + public boolean equals(Object obj) { + if (obj instanceof StateSymbolKey) { + StateSymbolKey s = (StateSymbolKey) obj; + // TODO: or equals? i think == should be ok, since we're working with unique instances + return Objects.equals(state, s.state) && Objects.equals(symbol, s.symbol); + } + return false; } + /** Returns the hash code. */ + public int hashCode() { + return hashCode; + } } diff --git a/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/semantics/Action.java b/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/semantics/Action.java index 1defb5a57..eaedef31e 100644 --- a/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/semantics/Action.java +++ b/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/semantics/Action.java @@ -1,132 +1,102 @@ package edu.tum.cup2.semantics; - import static edu.tum.cup2.semantics.SymbolValue.NoValue; -import java.lang.reflect.Method; - import edu.tum.cup2.parser.LRParser; import edu.tum.cup2.spec.exceptions.IllegalSpecException; import edu.tum.cup2.spec.util.RHSItem; +import java.lang.reflect.Method; +import java.util.Objects; /** - * Class around an action method. - * Replaced by closures later if possible. - * + * Class around an action method. Replaced by closures later if possible. + * * @author Andreas Wenger - * @author Michael Hausmann + * @author Michael Hausmann */ +public class Action implements RHSItem { + + protected transient Method + method; // can not be final as it has to be set after de-serializing the Action object + + /** + * @serial actionSubclass is actually used for persisting the Action subclass that represents the + * semantic action + * @since 1 + */ + protected Class actionSubclass; // this field is actually used for serialization + + protected transient int + paramsCount; // can not be final as it has to be set after de-serializing the Action object + protected transient boolean + returnsVoid; // can not be final as it has to be set after de-serializing the Action object + + private transient LRParser parserInstance = null; // associated parser instance + + /** Creates a new instance of a semantic action. */ + @SuppressWarnings("unchecked") + public Action() { + init((Class) getClass()); + } + + protected void init(Class myclass) { + actionSubclass = myclass; + if (myclass == null) return; + for (Method m : myclass.getMethods()) { + if (Objects.equals(m.getName(), "a")) { + this.method = m; + this.method.setAccessible(true); + this.paramsCount = this.method.getParameterTypes().length; + // we are not allowed to call a public method of an inner class, + // but we can circumvent that + this.returnsVoid = Objects.equals(method.getReturnType(), Void.TYPE); + return; + } + } + throw new IllegalSpecException("Action has no method called \"a\""); + } + + /** + * Executes the Action of this class. By default a method a(..) is searched over reflections and + * called with the current parameters. Don't forget to set the parameters previously! + * + * @return + */ + public Object doAction(Object[] parameters) throws Exception { + Method method = getMethod(); + Object ret = method.invoke(this, parameters); + if (isVoidReturn()) return NoValue; + else return ret; + } + + /** Gets the method assigned to this semantic action. */ + public Method getMethod() { + return method; + } + + /** Gets the number of parameters the method accepts. */ + public int getParamsCount() { + return paramsCount; + } + + /** Returns true, if the result type of the method is void. */ + public boolean isVoidReturn() { + return returnsVoid; + } + + /** Returns the Name of the Action class */ + protected String getActionSubclassName() { + if (null == this.actionSubclass) return null; + return this.actionSubclass.getName(); + } + + /** Returns the associated parser instance */ + public LRParser getParser() { + return this.parserInstance; + } -public class Action - implements RHSItem -{ - - protected transient Method method; //can not be final as it has to be set after de-serializing the Action object - - /** - * @serial actionSubclass is actually used for persisting the Action subclass that represents the semantic action - * @since 1 - */ - protected Class actionSubclass; //this field is actually used for serialization - - protected transient int paramsCount; //can not be final as it has to be set after de-serializing the Action object - protected transient boolean returnsVoid; //can not be final as it has to be set after de-serializing the Action object - - private transient LRParser parserInstance = null; //associated parser instance - - /** - * Creates a new instance of a semantic action. - */ - @SuppressWarnings("unchecked") - public Action() - { - init((Class) getClass()); - } - - protected void init(Class myclass) - { - actionSubclass = myclass; - if(myclass == null) return; - for (Method m : myclass.getMethods()) - { - if (m.getName().equals("a")) - { - this.method = m; - this.method.setAccessible(true); - this.paramsCount = this.method.getParameterTypes().length; - //we are not allowed to call a public method of an inner class, - //but we can circumvent that - this.returnsVoid = method.getReturnType().equals(Void.TYPE); - return; - } - } - throw new IllegalSpecException("Action has no method called \"a\""); - } - - - /** - * Executes the Action of this class. By default a method a(..) is searched - * over reflections and called with the current parameters. Don't forget to - * set the parameters previously! - * - * @return - */ - public Object doAction(Object[] parameters) throws Exception { - Method method = getMethod(); - Object ret = method.invoke(this, parameters); - if (isVoidReturn()) - return NoValue; - else - return ret; - } - - - /** - * Gets the method assigned to this semantic action. - */ - public Method getMethod() - { - return method; - } - - /** - * Gets the number of parameters the method accepts. - */ - public int getParamsCount() - { - return paramsCount; - } - - /** - * Returns true, if the result type of the method is void. - */ - public boolean isVoidReturn() - { - return returnsVoid; - } - - /** - * Returns the Name of the Action class - */ - protected String getActionSubclassName() - { - if(null == this.actionSubclass) return null; - return this.actionSubclass.getName(); - } - - /** - * Returns the associated parser instance - */ - public LRParser getParser() - { - return this.parserInstance; - } - - /** - * Sets the associated parser instance - */ - public void setParser(LRParser p) - { - this.parserInstance = p; - } + /** Sets the associated parser instance */ + public void setParser(LRParser p) { + this.parserInstance = p; + } } diff --git a/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/util/KTreeMap.java b/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/util/KTreeMap.java index b14c3111b..cd8c10fd9 100644 --- a/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/util/KTreeMap.java +++ b/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/util/KTreeMap.java @@ -3,765 +3,656 @@ import java.util.HashMap; import java.util.Iterator; import java.util.Map; +import java.util.Objects; import java.util.Stack; - /** - * This class is some kind of {@link Map} implementation based on a tree. For each dimension of the key, there might be - * a branch with a leaf storing a value. Its special ability is that it matches keys which are shorter then stored ones - * with the same beginning - but still unambiguous - to the value of the full key. null as key or value is - * omitted.
- *
+ * This class is some kind of {@link Map} implementation based on a tree. For each dimension of the + * key, there might be a branch with a leaf storing a value. Its special ability is that it matches + * keys which are shorter then stored ones with the same beginning - but still unambiguous - to the + * value of the full key. null as key or value is omitted.
+ *
* The structure of the tree is as follows: + * *
    - *
  • each node has between 0 and {@link #maxDimension} children
  • - *
  • each node represents a dimension of a stored key
  • - *
  • a node may have a own value; then it is a leaf (even if it has children!)
  • - *
  • nodes that are no leafs and only have one leaf in the branch under themselves store a reference to this leaf's - * value
  • - *
  • nodes that are no leafs and have more then on leaf in the branch(es) under themselves store null as - * value
    - *
  • + *
  • each node has between 0 and {@link #maxDimension} children + *
  • each node represents a dimension of a stored key + *
  • a node may have a own value; then it is a leaf (even if it has children!) + *
  • nodes that are no leafs and only have one leaf in the branch under themselves store a + * reference to this leaf's value + *
  • nodes that are no leafs and have more then on leaf in the branch(es) under themselves store + * null as value
    *
- *
- * When {@link #get(Iterable)} is called is traverses the tree by navigating with the values of its key-parts in the - * order its iterator presents them. When reaching the end of the key, the value of the current node is returned. This - * value is either: + * + *
+ * When {@link #get(Iterable)} is called is traverses the tree by navigating with the values of its + * key-parts in the order its iterator presents them. When reaching the end of the key, the value of + * the current node is returned. This value is either: + * *
    - *
  • null if there is no branch for this key
  • - *
  • The value which has been stored with this exact key
  • - *
  • The value which has been stored for some key longer then this - in the case this is the only leaf of the branch - * the given key has lead to
  • - *
  • null in the case that the branch the given key has lead to is ambigious (has more then one leaf)
  • + *
  • null if there is no branch for this key + *
  • The value which has been stored with this exact key + *
  • The value which has been stored for some key longer then this - in the case this is the + * only leaf of the branch the given key has lead to + *
  • null in the case that the branch the given key has lead to is ambigious (has + * more then one leaf) *
- *
- * This structure makes get and containsKey operations quite fast while putting longer keys or especially removing keys - * is more expensive.
- * The major disadvantage of this implementation: Because of obvious performance reasons this class uses arrays in each - * node to store references to its children instead of {@link HashMap}s. Therefore the single values of each key - * (keyparts) need to have integer representations which are bijective! (like enumerations for instance) This projection - * is delegated by using the {@link IEnumerator} interface. - * + * + *
+ * This structure makes get and containsKey operations quite fast while putting longer keys or + * especially removing keys is more expensive.
+ * The major disadvantage of this implementation: Because of obvious performance reasons this class + * uses arrays in each node to store references to its children instead of {@link HashMap}s. + * Therefore the single values of each key (keyparts) need to have integer representations which are + * bijective! (like enumerations for instance) This projection is delegated by using the {@link + * IEnumerator} interface. + * * @author Gero - * * @param Key (must be an instance of {@link Iterable} - *

+ *

* @param

The parts the key consists of * @param Value */ -public class KTreeMap, P, V> -{ - private final int maxDimension; - private final IEnumerator

enumerator; - - private Node root; - private int size = 0; - - - /** - * @param maxSize The maximal size of dimensions - * @throws IllegalArgumentException If maxSize > {@value #MAXIMUM_DIMENSION} - */ - public KTreeMap(int maxDimension, IEnumerator

enumerator) - { - this.maxDimension = maxDimension; - this.enumerator = enumerator; - - this.root = new Node(enumerator, maxDimension, null, null); - } - - - /** - * Puts a the given value under a branch defined by the given key - * - * @param key - * @param value - * @return Returns false if there has been a value assigned to this exact key before (and it !equals(value)) - * @throws NullPointerException If key or value are null! - * @throws IllegalArgumentException If the key iterator is emtpy - */ - public boolean put(K key, V value) - { - if (key == null || value == null) - { - throw new NullPointerException("Key and Value must not be null!!!"); - } - - final Iterator

it = key.iterator(); -// if (!it.hasNext()) -// { -// throw new IllegalArgumentException("Key must not be empty!!!"); -// } - - - // Try it with a loop... - final Stack> stack = new Stack>(); - Node node = this.root; - P lastKeyPart = null; - - // Descent - while (it.hasNext()) - { - lastKeyPart = it.next(); - node = node.getChildOrCreate(lastKeyPart, value); - stack.push(node); - } - if (!stack.isEmpty()) - { - stack.pop(); // Ensures that not is on top but its parent! - } - - - // At the bottom we are! - final boolean result; // Whether the value has been properly inserted (or it has been there before) - final boolean addedNewLeaf; - if (node.isLeaf) - { - // Sanity: Hash-collision or normal double-put??? - // // Only relevant if node is not root - // if (lastKeyPart != null) - // { - // if (!lastKeyPart.equals(node.keyPart)) - // { - // // keyPart failed the equals vs. hashCode-test, collision! - - // This node already is a leaf. Now we have to handle the isNode-special case - if (isRoot(node)) { - if (node.value == null) { - addedNewLeaf = true; - result = true; - } else { - addedNewLeaf = false; - result = value.equals(node.value); - } - } else { - // No root node: As it already isLeaf, result depends whether node.value equals value - addedNewLeaf = false; - result = value.equals(node.value); - } - - // } - // } - } else - { - result = true; - addedNewLeaf = true; - } - - node.isLeaf = true; - node.value = value; - - - // Ascend back to root - V propagatedValue = value; - - while (!stack.isEmpty()) - { - node = stack.pop(); - - node.leafCount += 1; - - // Updates of nodes value - if (node.isLeaf) - { - // Okay, a leafs value is fix. We just check whether we have to propagate value or null - if (propagatedValue != null && node.value.equals(propagatedValue)) - { - // Just keep propagatedValue - } else - { - // There seems to be an ambiguity from this point, set null! - propagatedValue = null; - } - } else - { - if (propagatedValue != null && node.value != null && node.value.equals(propagatedValue)) - { - // Just keep propagatedValue - } else - { - // We dont' know whether the ambiguity here is old (node.value == null) or new (propagatedValue == null - // || node.value.equals(propagatedValue), we just set null! - propagatedValue = null; - node.value = null; - } - } - } - - if (addedNewLeaf) - { - this.size += 1; - } - return result; - } - - - private boolean isRoot(Node node) - { - return node.keyPart == null; - } - - - /** - * @param key - * @return true if the key matches an unambiguous branch in the tree - */ - public boolean containsKey(K key) - { - return get(key) != null; - } - - - /** - * @param key - * @return true if this exact key is present in the tree - */ - public boolean containsKeyExact(K key) - { - return getExact(key) != null; - } - - - /** - * @param key - * @return true if the key matches an unambiguous value-branch in the tree - */ - public boolean containsValueFor(K key) - { - return getValue(key) != null; - } - - - /** - * Gets a value associated with the given key - or the value which has been put for a unambiguous longer key with - * the same beginning - * - * @param key - * @return See above - * @see #getNode(Iterable, boolean) - * @throws NullPointerException If the given key is null - */ - public V get(K key) - { - Node node = getNode(key, false); - if (node == null) - { - return null; - } else - { - return node.value; - } - } - - - /** - * Gets exactlly value associated with the given key - or null if there is none. - * - * @param key - * @return See above - * @see #getNode(Iterable, boolean) - * @throws NullPointerException If the given key is null - */ - public V getExact(K key) - { - Node node = getNode(key, true); - if (node == null) - { - return null; - } else - { - if (!node.isLeaf) - { - return null; - } - return node.value; - } - } - - - /** - * Gets a value associated with the given key, the value which has been put for a unambiguous longer key with - * the same beginning, or even the value associated with a shorter key if values are unambiguous. - * - * @param key - * @return See above - * @see #getNodeLazy(Iterable) - * @throws NullPointerException If the given key is null - */ - public V getValue(K key) - { - Node node = getNodeLazy(key); - if (node == null) - { - return null; - } else - { - return node.value; - } - } - - - private Node getNodeLazy(K key) - { - if (key == null) - { - throw new NullPointerException("Key must not be null!!!"); - } - - // Descent in the tree - Node node = this.root; - final Iterator

it = key.iterator(); - while (it.hasNext()) - { - final P keyPart = it.next(); - node = node.getChildWithNull(keyPart); - if (node == null) - { - return null; - } - - // Check for unambiguity of branch... - if ((node.leafCount == 1) && (!node.isLeaf) || ((node.leafCount == 0) && node.isLeaf)) - { - // Okay, narrow branch || real leaf! - return node; - } - } - - // Check for unambiguity of value... (possible even if branch has more then one leaf!) - if (node.value != null) - { - return node; - } - - // Ambigious! - return null; - } - - - private Node getNode(K key, boolean exact) - { - if (key == null) - { - throw new NullPointerException("Key must not be null!!!"); - } - - // Descent in the tree - Node node = this.root; - final Iterator

it = key.iterator(); - while (it.hasNext()) - { - final P keyPart = it.next(); - node = node.getChildWithNull(keyPart); - if (node == null) - { - return null; - } - - if (!exact) - { - // Check for unambiguity of branch... - if ((node.leafCount == 1) && (!node.isLeaf) || ((node.leafCount == 0) && node.isLeaf)) - { - // Okay, narrow branch || real leaf! - return node; - } - } - } - - // Check for leaf... - // if (exact) - // { - // if (node.isLeaf) - // { - // // Okay, real leaf! - // return node; - // } - // } else - // { - // // Check for unambiguity of branch... - // if () - // { - // // Okay, real leaf || narrow branch - // return node; - // } - // } - - // Ambigious! - return node; - } - - - /** - * Removes the given value from the tree - * - * @param key - * @return The value stored for the given key - * @throws NullPointerException If the given key is null - */ - public V remove(K key) - { - if (key == null) - { - throw new NullPointerException("Key must not be null!!!"); - } - - final Iterator

it = key.iterator(); -// if (!it.hasNext()) -// { -// return null; -// } - - - // Try it with a loop... - final Stack> stack = new Stack>(); - Node parent = null; - Node node = this.root; - - // Descent - while (it.hasNext()) - { - final P keyPart = it.next(); - final Node child = node.getChildWithNull(keyPart); - if (child == null) - { - // Easy: Key not found => jump off - return null; - } else - { - stack.push(node); - parent = node; - node = child; - } - } - if (!stack.isEmpty()) - { - stack.pop(); // Ensure that is at the top of the stack! - } - - - // Reached wanted node - final V result = node.value; - V propagatedValue; - if (node.isLeaf) - { - node.isLeaf = false; // So it will be deleted - propagatedValue = updateNode(parent, node, true, null); - } else - { - // It is not possible to remove not-put keys! - return null; - } - - - // Ascend - Node child = null; - while (!stack.isEmpty()) - { - child = node; - node = parent; - parent = stack.pop(); - - // Remove was successful, update values-references - node.leafCount -= 1; - - final boolean updateAfterRemove = (node.getChildWithNull(child.keyPart) == null); - propagatedValue = updateNode(parent, node, updateAfterRemove, propagatedValue); - } - - this.size -= 1; - - return result; - } - - - /** - * Updates the nodes value after its own value or one of its leafs' values has been removed. Used when ascending the - * branch. - * - * @param parent - * @param node - * @param completeUpdate - * @param newValue The value which is propagated from the lower part of the branch if it is narrow (leafCount == 1) - * or all leafes have the same value - * @return The new value to propagate up the tree - */ - private static V updateNode(Node parent, Node node, boolean completeUpdate, V newValue) - { - // 1. Check for remove - if (!node.isLeaf && node.leafCount == 0) - { - // Delete this! - if (parent != null) // Special case for root node!!! - { - parent.remove(node.keyPart); - } - return null; - } - // 2. Check for new value to set and/or propagate up the path - else - { - // Special case for root node! - if (parent == null) - { - return null; - } - - // At this point we got a problem: We have to decide which value we want to assign to the current node, but - // therefore we need to know the values of each children. - if (node.isLeaf) - { - // Gather values held by all direct children and compare them with the one of node - final Iterator> it = node.childrenIterator(); - V valueToCompare = node.value; - boolean valuesAreEqual = true; - while (it.hasNext()) - { - final Node oneChild = it.next(); - if (oneChild.value == null || !oneChild.value.equals(valueToCompare)) - { - valuesAreEqual = false; - break; - } - } - - if (valuesAreEqual) - { - // Values are equal, so it should be propagated up the branch! - return valueToCompare; - } else - { - return null; - } - } else - { - // Gather values held by all direct children and compare them - final Iterator> it = node.childrenIterator(); - V valueToCompare = null; - if (completeUpdate) - { - // In case we just removed a direct child: Don't care about newValue, but simply take the first of the - // other children - if (it.hasNext()) - { - valueToCompare = it.next().value; - } else - { - throw new IllegalStateException("Something went horribly wrong here... :-("); - } - } else - { - // If the remove operation is already some time ago, consider the propagated value! - valueToCompare = newValue; - } - while (valueToCompare != null && it.hasNext()) - { - final Node oneChild = it.next(); - if (oneChild.value == null || !oneChild.value.equals(valueToCompare)) - { - valueToCompare = null; - break; - } - } - - node.value = valueToCompare; - return valueToCompare; - } - } - } - - - /** - * Represents a dimension of a key in the {@link KTreeMap}. - * - * @author Gero - * - * @param

A part of the key representing on dimensions - * @param The value stored for a key - */ - static class Node - { - private final IEnumerator

enumerator; - private final Node[] table; - private final P keyPart; - private V value; - private int leafCount; - private boolean isLeaf; - - - /** - * Constructor for the root-node - * - * @param maxSize - * @param keyPart - * @param newValue - */ - Node(IEnumerator

enumerator, int maxSize, P keyPart, V newValue) - { - this.enumerator = enumerator; - this.table = new Node[maxSize]; - this.keyPart = keyPart; - - this.value = newValue; - this.isLeaf = true; // only root isLeaf from the start! - this.leafCount = 0; - } - - - private Node(IEnumerator

enumerator, int maxSize, P keyPart) - { - this.enumerator = enumerator; - this.table = new Node[maxSize]; - this.keyPart = keyPart; - - this.value = null; - this.isLeaf = false; - this.leafCount = 0; - } - - - @SuppressWarnings("unchecked") - private Node getChildOrCreate(P keyPart, V value) - { - final int index = enumerator.ordinal(keyPart, this.table.length - 1); - Node child = (Node) this.table[index]; - if (child == null) - { - child = new Node(enumerator, this.table.length, keyPart); - child.value = value; - this.table[index] = child; - } - return child; - } - - - @SuppressWarnings("unchecked") - private Node getChildWithNull(P keyPart) - { - final int index = enumerator.ordinal(keyPart, this.table.length - 1); - return (Node) this.table[index]; - } - - - private void remove(P keyPart) - { - final int index = enumerator.ordinal(keyPart, this.table.length - 1); - this.table[index] = null; - } - - - public Iterator> childrenIterator() - { - return new Iterator>() { - private int curIndex = -1; - private Node curNode = null; - private boolean hasToMove = true; - - - public boolean hasNext() - { - if (hasToMove) - { - return moveToNext(); - } - return curNode == null; - } - - - private boolean moveToNext() - { - hasToMove = false; - - final Node[] table = Node.this.table; - for (curIndex++; curIndex < table.length; curIndex++) - { - curNode = table[curIndex]; - if (curNode != null) - { - return true; - } - } - curNode = null; - return false; - } - - - @SuppressWarnings("unchecked") - public Node next() - { - if (hasToMove) - { - moveToNext(); - } - hasToMove = true; - return (Node) curNode; - } - - - public void remove() - { /* Not implemented */ - } - }; - } - - - @Override - public String toString() - { - return "[Node \n key: " + keyPart + "\n leafCount: " + leafCount + "\n isLeaf: " + Boolean.toString(isLeaf) - + "\n value: " + value + "\n]"; - } - } - - - public int getMaxDimensions() - { - return maxDimension; - } - - - public IEnumerator

getEnumerator() - { - return enumerator; - } - - - /** - * @return The number of stored values - */ - public int size() - { - return size; - } - - - public boolean isEmpty() - { - return size == 0; - } - - - public void clear() - { - this.root = new Node(enumerator, maxDimension, null, null); - this.size = 0; - } - - - /** - * Used in {@link KTreeMap} to delegate the generation of a bijective "hash" to the user of {@link KTreeMap} - * - * @author Gero - * - * @param

- */ - public interface IEnumerator

- { - /** - * @param keypart - * @param maxOrdinal - * @return A integer value 0 <= ordinal <= maxOrdinal which represents the given keypart - */ - public int ordinal(P keypart, int maxOrdinal); - } +public class KTreeMap, P, V> { + private final int maxDimension; + private final IEnumerator

enumerator; + + private Node root; + private int size = 0; + + /** + * @param maxSize The maximal size of dimensions + * @throws IllegalArgumentException If maxSize > {@value #MAXIMUM_DIMENSION} + */ + public KTreeMap(int maxDimension, IEnumerator

enumerator) { + this.maxDimension = maxDimension; + this.enumerator = enumerator; + + this.root = new Node(enumerator, maxDimension, null, null); + } + + /** + * Puts a the given value under a branch defined by the given key + * + * @param key + * @param value + * @return Returns false if there has been a value assigned to this exact key before + * (and it !equals(value)) + * @throws NullPointerException If key or value are null! + * @throws IllegalArgumentException If the key iterator is emtpy + */ + public boolean put(K key, V value) { + if (key == null || value == null) { + throw new NullPointerException("Key and Value must not be null!!!"); + } + + final Iterator

it = key.iterator(); + // if (!it.hasNext()) + // { + // throw new IllegalArgumentException("Key must not be empty!!!"); + // } + + // Try it with a loop... + final Stack> stack = new Stack>(); + Node node = this.root; + P lastKeyPart = null; + + // Descent + while (it.hasNext()) { + lastKeyPart = it.next(); + node = node.getChildOrCreate(lastKeyPart, value); + stack.push(node); + } + if (!stack.isEmpty()) { + stack.pop(); // Ensures that not is on top but its parent! + } + + // At the bottom we are! + final boolean + result; // Whether the value has been properly inserted (or it has been there before) + final boolean addedNewLeaf; + if (node.isLeaf) { + // Sanity: Hash-collision or normal double-put??? + // // Only relevant if node is not root + // if (lastKeyPart != null) + // { + // if (!lastKeyPart.equals(node.keyPart)) + // { + // // keyPart failed the equals vs. hashCode-test, collision! + + // This node already is a leaf. Now we have to handle the isNode-special case + if (isRoot(node)) { + if (node.value == null) { + addedNewLeaf = true; + result = true; + } else { + addedNewLeaf = false; + result = Objects.equals(value, node.value); + } + } else { + // No root node: As it already isLeaf, result depends whether node.value equals value + addedNewLeaf = false; + result = Objects.equals(value, node.value); + } + + // } + // } + } else { + result = true; + addedNewLeaf = true; + } + + node.isLeaf = true; + node.value = value; + + // Ascend back to root + V propagatedValue = value; + + while (!stack.isEmpty()) { + node = stack.pop(); + + node.leafCount += 1; + + // Updates of nodes value + if (node.isLeaf) { + // Okay, a leafs value is fix. We just check whether we have to propagate value or null + if (propagatedValue != null && Objects.equals(node.value, propagatedValue)) { + // Just keep propagatedValue + } else { + // There seems to be an ambiguity from this point, set null! + propagatedValue = null; + } + } else { + if (propagatedValue != null + && node.value != null + && Objects.equals(node.value, propagatedValue)) { + // Just keep propagatedValue + } else { + // We dont' know whether the ambiguity here is old (node.value == null) or new + // (propagatedValue == null + // || node.value.equals(propagatedValue), we just set null! + propagatedValue = null; + node.value = null; + } + } + } + + if (addedNewLeaf) { + this.size += 1; + } + return result; + } + + private boolean isRoot(Node node) { + return node.keyPart == null; + } + + /** + * @param key + * @return true if the key matches an unambiguous branch in the tree + */ + public boolean containsKey(K key) { + return get(key) != null; + } + + /** + * @param key + * @return true if this exact key is present in the tree + */ + public boolean containsKeyExact(K key) { + return getExact(key) != null; + } + + /** + * @param key + * @return true if the key matches an unambiguous value-branch in the tree + */ + public boolean containsValueFor(K key) { + return getValue(key) != null; + } + + /** + * Gets a value associated with the given key - or the value which has been put for a unambiguous + * longer key with the same beginning + * + * @param key + * @return See above + * @see #getNode(Iterable, boolean) + * @throws NullPointerException If the given key is null + */ + public V get(K key) { + Node node = getNode(key, false); + if (node == null) { + return null; + } else { + return node.value; + } + } + + /** + * Gets exactlly value associated with the given key - or null if there is none. + * + * @param key + * @return See above + * @see #getNode(Iterable, boolean) + * @throws NullPointerException If the given key is null + */ + public V getExact(K key) { + Node node = getNode(key, true); + if (node == null) { + return null; + } else { + if (!node.isLeaf) { + return null; + } + return node.value; + } + } + + /** + * Gets a value associated with the given key, the value which has been put for a unambiguous + * longer key with the same beginning, or even the value associated with a shorter key if values + * are unambiguous. + * + * @param key + * @return See above + * @see #getNodeLazy(Iterable) + * @throws NullPointerException If the given key is null + */ + public V getValue(K key) { + Node node = getNodeLazy(key); + if (node == null) { + return null; + } else { + return node.value; + } + } + + private Node getNodeLazy(K key) { + if (key == null) { + throw new NullPointerException("Key must not be null!!!"); + } + + // Descent in the tree + Node node = this.root; + final Iterator

it = key.iterator(); + while (it.hasNext()) { + final P keyPart = it.next(); + node = node.getChildWithNull(keyPart); + if (node == null) { + return null; + } + + // Check for unambiguity of branch... + if ((node.leafCount == 1) && (!node.isLeaf) || ((node.leafCount == 0) && node.isLeaf)) { + // Okay, narrow branch || real leaf! + return node; + } + } + + // Check for unambiguity of value... (possible even if branch has more then one leaf!) + if (node.value != null) { + return node; + } + + // Ambigious! + return null; + } + + private Node getNode(K key, boolean exact) { + if (key == null) { + throw new NullPointerException("Key must not be null!!!"); + } + + // Descent in the tree + Node node = this.root; + final Iterator

it = key.iterator(); + while (it.hasNext()) { + final P keyPart = it.next(); + node = node.getChildWithNull(keyPart); + if (node == null) { + return null; + } + + if (!exact) { + // Check for unambiguity of branch... + if ((node.leafCount == 1) && (!node.isLeaf) || ((node.leafCount == 0) && node.isLeaf)) { + // Okay, narrow branch || real leaf! + return node; + } + } + } + + // Check for leaf... + // if (exact) + // { + // if (node.isLeaf) + // { + // // Okay, real leaf! + // return node; + // } + // } else + // { + // // Check for unambiguity of branch... + // if () + // { + // // Okay, real leaf || narrow branch + // return node; + // } + // } + + // Ambigious! + return node; + } + + /** + * Removes the given value from the tree + * + * @param key + * @return The value stored for the given key + * @throws NullPointerException If the given key is null + */ + public V remove(K key) { + if (key == null) { + throw new NullPointerException("Key must not be null!!!"); + } + + final Iterator

it = key.iterator(); + // if (!it.hasNext()) + // { + // return null; + // } + + // Try it with a loop... + final Stack> stack = new Stack>(); + Node parent = null; + Node node = this.root; + + // Descent + while (it.hasNext()) { + final P keyPart = it.next(); + final Node child = node.getChildWithNull(keyPart); + if (child == null) { + // Easy: Key not found => jump off + return null; + } else { + stack.push(node); + parent = node; + node = child; + } + } + if (!stack.isEmpty()) { + stack.pop(); // Ensure that is at the top of the stack! + } + + // Reached wanted node + final V result = node.value; + V propagatedValue; + if (node.isLeaf) { + node.isLeaf = false; // So it will be deleted + propagatedValue = updateNode(parent, node, true, null); + } else { + // It is not possible to remove not-put keys! + return null; + } + + // Ascend + Node child = null; + while (!stack.isEmpty()) { + child = node; + node = parent; + parent = stack.pop(); + + // Remove was successful, update values-references + node.leafCount -= 1; + + final boolean updateAfterRemove = (node.getChildWithNull(child.keyPart) == null); + propagatedValue = updateNode(parent, node, updateAfterRemove, propagatedValue); + } + + this.size -= 1; + + return result; + } + + /** + * Updates the nodes value after its own value or one of its leafs' values has been removed. Used + * when ascending the branch. + * + * @param parent + * @param node + * @param completeUpdate + * @param newValue The value which is propagated from the lower part of the branch if it is narrow + * (leafCount == 1) or all leafes have the same value + * @return The new value to propagate up the tree + */ + private static V updateNode( + Node parent, Node node, boolean completeUpdate, V newValue) { + // 1. Check for remove + if (!node.isLeaf && node.leafCount == 0) { + // Delete this! + if (parent != null) // Special case for root node!!! + { + parent.remove(node.keyPart); + } + return null; + } + // 2. Check for new value to set and/or propagate up the path + else { + // Special case for root node! + if (parent == null) { + return null; + } + + // At this point we got a problem: We have to decide which value we want to assign to the + // current node, but + // therefore we need to know the values of each children. + if (node.isLeaf) { + // Gather values held by all direct children and compare them with the one of node + final Iterator> it = node.childrenIterator(); + V valueToCompare = node.value; + boolean valuesAreEqual = true; + while (it.hasNext()) { + final Node oneChild = it.next(); + if (oneChild.value == null || !Objects.equals(oneChild.value, valueToCompare)) { + valuesAreEqual = false; + break; + } + } + + if (valuesAreEqual) { + // Values are equal, so it should be propagated up the branch! + return valueToCompare; + } else { + return null; + } + } else { + // Gather values held by all direct children and compare them + final Iterator> it = node.childrenIterator(); + V valueToCompare = null; + if (completeUpdate) { + // In case we just removed a direct child: Don't care about newValue, but simply take the + // first of the + // other children + if (it.hasNext()) { + valueToCompare = it.next().value; + } else { + throw new IllegalStateException("Something went horribly wrong here... :-("); + } + } else { + // If the remove operation is already some time ago, consider the propagated value! + valueToCompare = newValue; + } + while (valueToCompare != null && it.hasNext()) { + final Node oneChild = it.next(); + if (oneChild.value == null || !Objects.equals(oneChild.value, valueToCompare)) { + valueToCompare = null; + break; + } + } + + node.value = valueToCompare; + return valueToCompare; + } + } + } + + /** + * Represents a dimension of a key in the {@link KTreeMap}. + * + * @author Gero + * @param

A part of the key representing on dimensions + * @param The value stored for a key + */ + static class Node { + private final IEnumerator

enumerator; + private final Node[] table; + private final P keyPart; + private V value; + private int leafCount; + private boolean isLeaf; + + /** + * Constructor for the root-node + * + * @param maxSize + * @param keyPart + * @param newValue + */ + Node(IEnumerator

enumerator, int maxSize, P keyPart, V newValue) { + this.enumerator = enumerator; + this.table = new Node[maxSize]; + this.keyPart = keyPart; + + this.value = newValue; + this.isLeaf = true; // only root isLeaf from the start! + this.leafCount = 0; + } + + private Node(IEnumerator

enumerator, int maxSize, P keyPart) { + this.enumerator = enumerator; + this.table = new Node[maxSize]; + this.keyPart = keyPart; + + this.value = null; + this.isLeaf = false; + this.leafCount = 0; + } + + @SuppressWarnings("unchecked") + private Node getChildOrCreate(P keyPart, V value) { + final int index = enumerator.ordinal(keyPart, this.table.length - 1); + Node child = (Node) this.table[index]; + if (child == null) { + child = new Node(enumerator, this.table.length, keyPart); + child.value = value; + this.table[index] = child; + } + return child; + } + + @SuppressWarnings("unchecked") + private Node getChildWithNull(P keyPart) { + final int index = enumerator.ordinal(keyPart, this.table.length - 1); + return (Node) this.table[index]; + } + + private void remove(P keyPart) { + final int index = enumerator.ordinal(keyPart, this.table.length - 1); + this.table[index] = null; + } + + public Iterator> childrenIterator() { + return new Iterator>() { + private int curIndex = -1; + private Node curNode = null; + private boolean hasToMove = true; + + public boolean hasNext() { + if (hasToMove) { + return moveToNext(); + } + return curNode == null; + } + + private boolean moveToNext() { + hasToMove = false; + + final Node[] table = Node.this.table; + for (curIndex++; curIndex < table.length; curIndex++) { + curNode = table[curIndex]; + if (curNode != null) { + return true; + } + } + curNode = null; + return false; + } + + @SuppressWarnings("unchecked") + public Node next() { + if (hasToMove) { + moveToNext(); + } + hasToMove = true; + return (Node) curNode; + } + + public void remove() { + /* Not implemented */ + } + }; + } + + @Override + public String toString() { + return "[Node \n key: " + + keyPart + + "\n leafCount: " + + leafCount + + "\n isLeaf: " + + Boolean.toString(isLeaf) + + "\n value: " + + value + + "\n]"; + } + } + + public int getMaxDimensions() { + return maxDimension; + } + + public IEnumerator

getEnumerator() { + return enumerator; + } + + /** @return The number of stored values */ + public int size() { + return size; + } + + public boolean isEmpty() { + return size == 0; + } + + public void clear() { + this.root = new Node(enumerator, maxDimension, null, null); + this.size = 0; + } + + /** + * Used in {@link KTreeMap} to delegate the generation of a bijective "hash" to the user of {@link + * KTreeMap} + * + * @author Gero + * @param

+ */ + public interface IEnumerator

{ + /** + * @param keypart + * @param maxOrdinal + * @return A integer value 0 <= ordinal <= maxOrdinal which represents the given keypart + */ + public int ordinal(P keypart, int maxOrdinal); + } } diff --git a/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/util/Reflection.java b/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/util/Reflection.java index 612e8ef7e..eebc36d82 100644 --- a/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/util/Reflection.java +++ b/testsuite/testcases/src/test/cases/cup2private/edu/tum/cup2/util/Reflection.java @@ -1,12 +1,5 @@ package edu.tum.cup2.util; -import java.lang.reflect.Constructor; -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.util.List; - import edu.tum.cup2.grammar.AuxiliaryLHS4SemanticShiftAction; import edu.tum.cup2.grammar.NonTerminal; import edu.tum.cup2.grammar.SpecialTerminals; @@ -14,559 +7,490 @@ import edu.tum.cup2.grammar.Terminal; import edu.tum.cup2.parser.LRParser; import edu.tum.cup2.semantics.Action; +import edu.tum.cup2.semantics.ErrorInformation; import edu.tum.cup2.semantics.SymbolValue; import edu.tum.cup2.semantics.SymbolValueClasses; import edu.tum.cup2.spec.CUP2Specification; import edu.tum.cup2.spec.exceptions.IllegalSpecException; -import edu.tum.cup2.semantics.ErrorInformation; - +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.List; +import java.util.Objects; /** - * This is the "secret" slum of this project. - * All nasty methods using reflection reside here. - * But don't forget, that as so often in reality - * it's the small people which make this world possible - * in which we live. + * This is the "secret" slum of this project. All nasty methods using reflection reside here. But + * don't forget, that as so often in reality it's the small people which make this world possible in + * which we live. * * @author Andreas Wenger * @author Stefan Dangl */ -public class Reflection -{ - - - /** - * Gets an array of all {@link Terminal}s that are defined in the enum - * "Terminals" in the first class that extends {@link CUP2Specification} in - * the current stack trace. - */ - public static Terminal[] getTerminals() - { - try - { - //get terminals as Objects - Object[] terminals = getEnumElements("Terminals"); - //convert each of the values to a Terminal - Terminal[] ret = new Terminal[terminals.length]; - for (int i = 0; i < ret.length; i++) - { - try - { - ret[i] = (Terminal) terminals[i]; - } - catch (ClassCastException ex) - { - throw new IllegalSpecException("The enum called \"Terminals\" must implement " + - "the Terminal interface!"); - } - } - return ret; - } - catch (Exception ex) - { - //TODO: global error handling - ex.printStackTrace(); - System.exit(0); - return null; - } - } - - - /** - * Gets an array of all {@link NonTerminal}s that are defined in the enum - * "NonTerminals" in the first class that extends {@link CUP2Specification} in - * the current stack trace. - */ - public static NonTerminal[] getNonTerminals() - { - try - { - //get terminals as Objects - Object[] terminals = getEnumElements("NonTerminals"); - //convert each of the values to a NonTerminals - NonTerminal[] ret = new NonTerminal[terminals.length]; - for (int i = 0; i < ret.length; i++) - { - try - { - ret[i] = (NonTerminal) terminals[i]; - } - catch (ClassCastException ex) - { - //TODO: illegalspecexception - throw new Exception("The enum called \"NonTerminals\" must implement the NonTerminal interface!"); - } - } - return ret; - } - catch (Exception ex) - { - //TODO: global error handling - ex.printStackTrace(); - System.exit(0); - return null; - } - } - - - /** - * Gets an array of all elements that are defined in the enum - * with the given name in the first class that extends {@link CUP2Specification} in - * the current stack trace. - */ - @SuppressWarnings("unchecked") private static Object[] getEnumElements(String name) - { - try - { - //get the spec class - Class specClass = getSpecClass(); - //get the enum - Object elements[] = null; - Class[] childClasses = specClass.getClasses(); - for (int i = 0; i < childClasses.length; i++) - { - Class childClass = childClasses[i]; - if (childClass.getSimpleName().equals(name)) - { - elements = childClass.getEnumConstants(); - break; - } - } - //if terminals is still null, there was no enum - if (elements == null) - { - //TODO: illegalspecexception - throw new Exception("Specification is missing an enum called \"" + name + "\"!"); - } - return elements; - } - catch (Exception ex) - { - //TODO: global error handling - ex.printStackTrace(); - System.exit(0); - return null; - } - } - - - /** - * Gets the first class that extends {@link CUP2Specification} in - * the current stack trace. - */ - @SuppressWarnings("unchecked") public static Class getSpecClass() - { - try - { - StackTraceElement[] stack = Thread.currentThread().getStackTrace(); - for (StackTraceElement ste : new It(stack)) - { - String className = ste.getClassName(); - Class classClass = Class.forName(className); - if (!classClass.equals(CUP2Specification.class) && CUP2Specification.class.isAssignableFrom(classClass)) - { - //subclass of CUPSpecification found - return classClass; - } - } - throw new Exception("No class extending CUPSpecification in stack trace!"); - } - catch (Exception ex) - { - //TODO: global error handling - ex.printStackTrace(); - System.exit(0); - return null; - } - } - - /** - * Gets the first occurrence of class {@link LRParser} in - * the current stack trace. - */ - @SuppressWarnings("unchecked") public static StackTraceElement getLRParserClass() - { - try - { - StackTraceElement[] stack = Thread.currentThread().getStackTrace(); - for (StackTraceElement ste : new It(stack)) - { - Class classClass = ste.getClass(); - if (!classClass.equals(LRParser.class)) - { - //LRParser found - return ste; - } - } - throw new Exception("NO occurance of LRParser in stack trace!"); - } - catch (Exception ex) - { - //TODO: global error handling - ex.printStackTrace(); - System.exit(0); - return null; - } - } - - - /** - * Gets the symbol-value-binding classes in a hashmap with the symbols as keys. - * These classes are found in the first class that extends {@link CUP2Specification} in - * the current stack trace. - */ - @SuppressWarnings("unchecked") public static SymbolValueClasses - getSymbolValueClasses(Terminal[] terminals, NonTerminal[] nonTerminals) - { - return getSymbolValueClasses(terminals, nonTerminals, getSpecClass()); - } - - /** - * @see {@link Reflection#getSymbolValueClasses(Terminal[], NonTerminal[])} - */ - @SuppressWarnings("unchecked") public static SymbolValueClasses - getSymbolValueClasses(Terminal[] terminals, NonTerminal[] nonTerminals, Class specClass) - { - try - { - //get all classes that extend the SymbolValue class - SymbolValueClasses ret = new SymbolValueClasses(); - Class[] childClasses = specClass.getClasses(); - for (int i = 0; i < childClasses.length; i++) - { - Class childClass = childClasses[i]; - if (SymbolValue.class.isAssignableFrom(childClass)) - { - //the symbol is the terminal/nonterminal with the same name - Symbol symbol = getSymbolByName(childClass.getSimpleName(), terminals, nonTerminals); - if (symbol == null) - { - throw new IllegalSpecException("The SymbolValue \"" + childClass.getSimpleName() + "\" belongs " + - "to none of the available terminals/nonterminals, but it must have the same name."); - } - ret.put(symbol, childClass); - } - } - return ret; - } - catch (Exception ex) - { - //TODO: global error handling - ex.printStackTrace(); - System.exit(0); - return null; - } - } - - - /** - * Returns the terminal or nonterminal with the given name from - * the given lists of terminals and nonterminals. If not existing, null is returned. - */ - private static Symbol getSymbolByName(String name, Terminal[] terminals, NonTerminal[] nonTerminals) - { - for (Terminal e : new It(terminals)) - { - if (e.toString().equals(name)) - return e; - } - for (NonTerminal e : new It(nonTerminals)) - { - if (e.toString().equals(name)) - return e; - } - return null; - } - - - /** - * Returns the position for the given {@link Action} (i.e. how many - * symbols have to be shifted before this action is called). - * OBSOLETE - */ - @Deprecated @SuppressWarnings("unchecked") public static int getActionPosition(Action action, List rhsSymbols, - SymbolValueClasses symbolValueClasses) - { - try - { - //gets the method named "a" from the given action - Method method = null; - Method[] methods = action.getClass().getMethods(); - for (int i = 0; i < methods.length; i++) - { - Method m = methods[i]; - if (m.getName().equals("a")) - { - method = m; - break; - } - } - if (method == null) - { - throw new IllegalSpecException("All actions must have exactly one method called \"a\"!"); - } - //get the parameters of this method - Class[] parameters = method.getParameterTypes(); - //for each parameter, find the appropriate symbol-value (go on if not matching, - //since unneeded symbols can be left out). when we have gone - //through all parameters, we know the position. - int position = 0; - It rhs = new It(rhsSymbols); - for (int i = 0; i < parameters.length; i++) - { - //class of action parameter - Class paramClass = parameters[i]; - //find corresponding RHS symbol - while (true) - { - if (!rhs.hasNext()) - { - throw new IllegalSpecException("Parameters do not match for an action for the RHS: " + rhsSymbols.toString()); - } - Symbol rhsSymbol = rhs.next(); - position++; - //first option: parameter stands for a symbol-value-binding - //this is the case when the parameter class is the same as the rhsSymbol's value class - Class> symbolClass = symbolValueClasses.get(rhsSymbol); - if (symbolClass != null && paramClass.equals( - ((ParameterizedType) symbolClass.getGenericSuperclass()).getActualTypeArguments()[0])) //TODO: UGLY! UGLY! UGLY! - break; - //second option: parameter stands for a terminal/nonterminal (name doesn't matter) - if (paramClass.isAssignableFrom(Terminal.class) || paramClass.isAssignableFrom(NonTerminal.class)) - break; - //third option: current symbol was left out, parameter stands for a later symbol: go on - } - } - return position; - } - catch (Exception ex) - { - //TODO: global error handling - ex.printStackTrace(); - System.exit(0); - return 0; - } - } - - - /** - * Checks, if the given action {@link Action} fits to the given position (i.e. how many - * symbols have to be shifted before this action is called). If it is ok, nothing happens, - * but if it does not fit (i.e. the action is invalid or if its parameters do not fit to the - * symbol-values of the production up to the given position) an {@link IllegalSpecException} is thrown. - * - * TODO : very strict, should also check possible casts. - */ - public static void checkAction(Action action, int position, - List rhsSymbols, SymbolValueClasses symbolValueClasses) - { - try - { - //gets the method named "a" from the given action - Method method = null; - Method[] methods = action.getClass().getMethods(); - for (int i = 0; i < methods.length; i++) - { - Method m = methods[i]; - if (m.getName().equals("a")) - { - method = m; - break; - } - } - if (method == null) - { - throw new IllegalSpecException("All actions must have exactly one method called \"a\"!"); - } - - // do the checking - checkParamsOfAction(method, position, rhsSymbols, symbolValueClasses); - - } - catch (Exception ex) - { - //TODO - ex.printStackTrace(); - throw new IllegalSpecException(ex.getMessage()); - } - } - - - /** - * Checks the parameters of the method method if they are compatible with - * the expected values. Note that there is a special checking for primitive types which - * can occur if the specification is written in scala. - * - * @param method - * @param position - * @param rhsSymbols - * @param symbolValueClasses - * @throws Exception - */ - public static void checkParamsOfAction(Method method, int position, - List rhsSymbols, SymbolValueClasses symbolValueClasses) throws Exception { - - //get the parameters of this method - Type[] parameters = method.getGenericParameterTypes(); - //for each parameter, find the appropriate symbol-value (go on if not matching, - //since unneeded symbols can be left out). - //when we have gone through all parameters, it is ok if we have not exceeded the given position. - int curPosition = 0; - It rhs = new It(rhsSymbols); - for (int i = 0; i < parameters.length; i++) - { - //type of action parameter - Type paramType = parameters[i]; - //find corresponding RHS symbol - while (true) - { - if (!rhs.hasNext()) - { - throw new IllegalSpecException("Parameters do not match for an action for the RHS: " + rhsSymbols.toString()); - } - Symbol rhsSymbol = rhs.next(); - curPosition++; - if (curPosition > position) - { - throw new IllegalSpecException("The action occurs too early or it's method requires too much parameters: " + rhsSymbols.toString()); - } - //does parameter stand for a symbol-value-binding? - //this is the case when the parameter class is the same as the rhsSymbol's value class - Class> symbolClass = symbolValueClasses.get(rhsSymbol); - if (symbolClass != null) - { - // rhsSymbol has symbol-value-binding => parameter MUST HAVE - // equal type. - Type expectedType = ((ParameterizedType) symbolClass - .getGenericSuperclass()).getActualTypeArguments()[0]; - if (expectedType instanceof ParameterizedType) - expectedType = ((ParameterizedType) expectedType) - .getRawType(); - if (paramType instanceof ParameterizedType) - paramType = ((ParameterizedType) paramType) - .getRawType(); - // System.out.println(paramType+" == "+expectedType); - - Class resolvedClass = resolvePrimitiveTypes(((Class) paramType)); - - if (resolvedClass.isAssignableFrom((Class) expectedType)) - break; - else { - - throw new IllegalSpecException( - "Parameters do not match for an action for the RHS: " - + rhsSymbols.toString() + "(expected " - + expectedType + ", found " + paramType - + ")"); - } - } - if (rhsSymbol instanceof AuxiliaryLHS4SemanticShiftAction) - { - // rhsSymbol has no symbol-value-binding, but is an auxiliary NT => - // if auxNT has symbol-value void ignore auxiliary NT, - // otherwise parameter MUST BE symbolvaluetype of aux NT. - AuxiliaryLHS4SemanticShiftAction auxNT = (AuxiliaryLHS4SemanticShiftAction)rhsSymbol; - if (auxNT.symbolValueType==Void.TYPE) - continue; - if (((Class)paramType).isAssignableFrom(auxNT.symbolValueType)) - break; - else +public class Reflection { + + /** + * Gets an array of all {@link Terminal}s that are defined in the enum "Terminals" in the first + * class that extends {@link CUP2Specification} in the current stack trace. + */ + public static Terminal[] getTerminals() { + try { + // get terminals as Objects + Object[] terminals = getEnumElements("Terminals"); + // convert each of the values to a Terminal + Terminal[] ret = new Terminal[terminals.length]; + for (int i = 0; i < ret.length; i++) { + try { + ret[i] = (Terminal) terminals[i]; + } catch (ClassCastException ex) { throw new IllegalSpecException( - "Parameters do not match for an action for the RHS: " + rhsSymbols.toString() + - "(expected "+auxNT.symbolValueType+", found "+paramType+")" - ); + "The enum called \"Terminals\" must implement " + "the Terminal interface!"); + } + } + return ret; + } catch (Exception ex) { + // TODO: global error handling + ex.printStackTrace(); + System.exit(0); + return null; + } + } + + /** + * Gets an array of all {@link NonTerminal}s that are defined in the enum "NonTerminals" in the + * first class that extends {@link CUP2Specification} in the current stack trace. + */ + public static NonTerminal[] getNonTerminals() { + try { + // get terminals as Objects + Object[] terminals = getEnumElements("NonTerminals"); + // convert each of the values to a NonTerminals + NonTerminal[] ret = new NonTerminal[terminals.length]; + for (int i = 0; i < ret.length; i++) { + try { + ret[i] = (NonTerminal) terminals[i]; + } catch (ClassCastException ex) { + // TODO: illegalspecexception + throw new Exception( + "The enum called \"NonTerminals\" must implement the NonTerminal interface!"); + } + } + return ret; + } catch (Exception ex) { + // TODO: global error handling + ex.printStackTrace(); + System.exit(0); + return null; + } + } + + /** + * Gets an array of all elements that are defined in the enum with the given name in the first + * class that extends {@link CUP2Specification} in the current stack trace. + */ + @SuppressWarnings("unchecked") + private static Object[] getEnumElements(String name) { + try { + // get the spec class + Class specClass = getSpecClass(); + // get the enum + Object elements[] = null; + Class[] childClasses = specClass.getClasses(); + for (int i = 0; i < childClasses.length; i++) { + Class childClass = childClasses[i]; + if (Objects.equals(childClass.getSimpleName(), name)) { + elements = childClass.getEnumConstants(); + break; + } + } + // if terminals is still null, there was no enum + if (elements == null) { + // TODO: illegalspecexception + throw new Exception("Specification is missing an enum called \"" + name + "\"!"); + } + return elements; + } catch (Exception ex) { + // TODO: global error handling + ex.printStackTrace(); + System.exit(0); + return null; + } + } + + /** Gets the first class that extends {@link CUP2Specification} in the current stack trace. */ + @SuppressWarnings("unchecked") + public static Class getSpecClass() { + try { + StackTraceElement[] stack = Thread.currentThread().getStackTrace(); + for (StackTraceElement ste : new It(stack)) { + String className = ste.getClassName(); + Class classClass = Class.forName(className); + if (!Objects.equals(classClass, CUP2Specification.class) + && CUP2Specification.class.isAssignableFrom(classClass)) { + // subclass of CUPSpecification found + return classClass; + } + } + throw new Exception("No class extending CUPSpecification in stack trace!"); + } catch (Exception ex) { + // TODO: global error handling + ex.printStackTrace(); + System.exit(0); + return null; + } + } + + /** Gets the first occurrence of class {@link LRParser} in the current stack trace. */ + @SuppressWarnings("unchecked") + public static StackTraceElement getLRParserClass() { + try { + StackTraceElement[] stack = Thread.currentThread().getStackTrace(); + for (StackTraceElement ste : new It(stack)) { + Class classClass = ste.getClass(); + if (!Objects.equals(classClass, LRParser.class)) { + // LRParser found + return ste; + } + } + throw new Exception("NO occurance of LRParser in stack trace!"); + } catch (Exception ex) { + // TODO: global error handling + ex.printStackTrace(); + System.exit(0); + return null; + } + } + + /** + * Gets the symbol-value-binding classes in a hashmap with the symbols as keys. These classes are + * found in the first class that extends {@link CUP2Specification} in the current stack trace. + */ + @SuppressWarnings("unchecked") + public static SymbolValueClasses getSymbolValueClasses( + Terminal[] terminals, NonTerminal[] nonTerminals) { + return getSymbolValueClasses(terminals, nonTerminals, getSpecClass()); + } + + /** @see {@link Reflection#getSymbolValueClasses(Terminal[], NonTerminal[])} */ + @SuppressWarnings("unchecked") + public static SymbolValueClasses getSymbolValueClasses( + Terminal[] terminals, NonTerminal[] nonTerminals, Class specClass) { + try { + // get all classes that extend the SymbolValue class + SymbolValueClasses ret = new SymbolValueClasses(); + Class[] childClasses = specClass.getClasses(); + for (int i = 0; i < childClasses.length; i++) { + Class childClass = childClasses[i]; + if (SymbolValue.class.isAssignableFrom(childClass)) { + // the symbol is the terminal/nonterminal with the same name + Symbol symbol = getSymbolByName(childClass.getSimpleName(), terminals, nonTerminals); + if (symbol == null) { + throw new IllegalSpecException( + "The SymbolValue \"" + + childClass.getSimpleName() + + "\" belongs " + + "to none of the available terminals/nonterminals, but it must have the same name."); + } + ret.put(symbol, childClass); + } + } + return ret; + } catch (Exception ex) { + // TODO: global error handling + ex.printStackTrace(); + System.exit(0); + return null; + } + } + + /** + * Returns the terminal or nonterminal with the given name from the given lists of terminals and + * nonterminals. If not existing, null is returned. + */ + private static Symbol getSymbolByName( + String name, Terminal[] terminals, NonTerminal[] nonTerminals) { + for (Terminal e : new It(terminals)) { + if (Objects.equals(e.toString(), name)) return e; + } + for (NonTerminal e : new It(nonTerminals)) { + if (Objects.equals(e.toString(), name)) return e; + } + return null; + } + + /** + * Returns the position for the given {@link Action} (i.e. how many symbols have to be shifted + * before this action is called). OBSOLETE + */ + @Deprecated + @SuppressWarnings("unchecked") + public static int getActionPosition( + Action action, List rhsSymbols, SymbolValueClasses symbolValueClasses) { + try { + // gets the method named "a" from the given action + Method method = null; + Method[] methods = action.getClass().getMethods(); + for (int i = 0; i < methods.length; i++) { + Method m = methods[i]; + if (Objects.equals(m.getName(), "a")) { + method = m; + break; + } + } + if (method == null) { + throw new IllegalSpecException("All actions must have exactly one method called \"a\"!"); + } + // get the parameters of this method + Class[] parameters = method.getParameterTypes(); + // for each parameter, find the appropriate symbol-value (go on if not matching, + // since unneeded symbols can be left out). when we have gone + // through all parameters, we know the position. + int position = 0; + It rhs = new It(rhsSymbols); + for (int i = 0; i < parameters.length; i++) { + // class of action parameter + Class paramClass = parameters[i]; + // find corresponding RHS symbol + while (true) { + if (!rhs.hasNext()) { + throw new IllegalSpecException( + "Parameters do not match for an action for the RHS: " + rhsSymbols.toString()); + } + Symbol rhsSymbol = rhs.next(); + position++; + // first option: parameter stands for a symbol-value-binding + // this is the case when the parameter class is the same as the rhsSymbol's value class + Class> symbolClass = symbolValueClasses.get(rhsSymbol); + if (symbolClass != null + && Objects.equals( + paramClass, + ((ParameterizedType) symbolClass.getGenericSuperclass()) + .getActualTypeArguments()[0])) // TODO: UGLY! UGLY! UGLY! + break; + // second option: parameter stands for a terminal/nonterminal (name doesn't matter) + if (paramClass.isAssignableFrom(Terminal.class) + || paramClass.isAssignableFrom(NonTerminal.class)) break; + // third option: current symbol was left out, parameter stands for a later symbol: go on + } } - if (SpecialTerminals.Error.equals(rhsSymbol)) - { - // rhsSymbol has no symbol-value-binding, is no auxiliary NT, but is an Error => parameter MUST BE ErrorInformation - if(paramType.equals(ErrorInformation.class)) + return position; + } catch (Exception ex) { + // TODO: global error handling + ex.printStackTrace(); + System.exit(0); + return 0; + } + } + + /** + * Checks, if the given action {@link Action} fits to the given position (i.e. how many symbols + * have to be shifted before this action is called). If it is ok, nothing happens, but if it does + * not fit (i.e. the action is invalid or if its parameters do not fit to the symbol-values of the + * production up to the given position) an {@link IllegalSpecException} is thrown. + * + *

TODO : very strict, should also check possible casts. + */ + public static void checkAction( + Action action, int position, List rhsSymbols, SymbolValueClasses symbolValueClasses) { + try { + // gets the method named "a" from the given action + Method method = null; + Method[] methods = action.getClass().getMethods(); + for (int i = 0; i < methods.length; i++) { + Method m = methods[i]; + if (Objects.equals(m.getName(), "a")) { + method = m; break; - else + } + } + if (method == null) { + throw new IllegalSpecException("All actions must have exactly one method called \"a\"!"); + } + + // do the checking + checkParamsOfAction(method, position, rhsSymbols, symbolValueClasses); + + } catch (Exception ex) { + // TODO + ex.printStackTrace(); + throw new IllegalSpecException(ex.getMessage()); + } + } + + /** + * Checks the parameters of the method method if they are compatible with the + * expected values. Note that there is a special checking for primitive types which can occur if + * the specification is written in scala. + * + * @param method + * @param position + * @param rhsSymbols + * @param symbolValueClasses + * @throws Exception + */ + public static void checkParamsOfAction( + Method method, int position, List rhsSymbols, SymbolValueClasses symbolValueClasses) + throws Exception { + + // get the parameters of this method + Type[] parameters = method.getGenericParameterTypes(); + // for each parameter, find the appropriate symbol-value (go on if not matching, + // since unneeded symbols can be left out). + // when we have gone through all parameters, it is ok if we have not exceeded the given + // position. + int curPosition = 0; + It rhs = new It(rhsSymbols); + for (int i = 0; i < parameters.length; i++) { + // type of action parameter + Type paramType = parameters[i]; + // find corresponding RHS symbol + while (true) { + if (!rhs.hasNext()) { + throw new IllegalSpecException( + "Parameters do not match for an action for the RHS: " + rhsSymbols.toString()); + } + Symbol rhsSymbol = rhs.next(); + curPosition++; + if (curPosition > position) { + throw new IllegalSpecException( + "The action occurs too early or it's method requires too much parameters: " + + rhsSymbols.toString()); + } + // does parameter stand for a symbol-value-binding? + // this is the case when the parameter class is the same as the rhsSymbol's value class + Class> symbolClass = symbolValueClasses.get(rhsSymbol); + if (symbolClass != null) { + // rhsSymbol has symbol-value-binding => parameter MUST HAVE + // equal type. + Type expectedType = + ((ParameterizedType) symbolClass.getGenericSuperclass()).getActualTypeArguments()[0]; + if (expectedType instanceof ParameterizedType) + expectedType = ((ParameterizedType) expectedType).getRawType(); + if (paramType instanceof ParameterizedType) + paramType = ((ParameterizedType) paramType).getRawType(); + // System.out.println(paramType+" == "+expectedType); + + Class resolvedClass = resolvePrimitiveTypes(((Class) paramType)); + + if (resolvedClass.isAssignableFrom((Class) expectedType)) break; + else { + + throw new IllegalSpecException( + "Parameters do not match for an action for the RHS: " + + rhsSymbols.toString() + + "(expected " + + expectedType + + ", found " + + paramType + + ")"); + } + } + if (rhsSymbol instanceof AuxiliaryLHS4SemanticShiftAction) { + // rhsSymbol has no symbol-value-binding, but is an auxiliary NT => + // if auxNT has symbol-value void ignore auxiliary NT, + // otherwise parameter MUST BE symbolvaluetype of aux NT. + AuxiliaryLHS4SemanticShiftAction auxNT = (AuxiliaryLHS4SemanticShiftAction) rhsSymbol; + if (auxNT.symbolValueType == Void.TYPE) continue; + if (((Class) paramType).isAssignableFrom(auxNT.symbolValueType)) break; + else + throw new IllegalSpecException( + "Parameters do not match for an action for the RHS: " + + rhsSymbols.toString() + + "(expected " + + auxNT.symbolValueType + + ", found " + + paramType + + ")"); + } + if (Objects.equals(SpecialTerminals.Error, rhsSymbol)) { + // rhsSymbol has no symbol-value-binding, is no auxiliary NT, but is an Error => parameter + // MUST BE ErrorInformation + if (Objects.equals(paramType, ErrorInformation.class)) break; + else + throw new IllegalSpecException( + "Parameters do not match for an action for the RHS: " + + rhsSymbols.toString() + + "(expected ErrorInformation, found " + + paramType + + ")"); + } + // rhsSymbol is neither value-binded, an aux NT nor an Error => It has no meaning at all + // (for example Epsilon) + } + } + // given position must be at least as high as the computed minimal position + if (curPosition < position) { + while (curPosition < position) { + if (!rhs.hasNext()) throw new IllegalSpecException( - "Parameters do not match for an action for the RHS: " + rhsSymbols.toString() + - "(expected ErrorInformation, found "+paramType+")" - ); + "Parameters do not match for an action for the RHS: " + rhsSymbols.toString()); + Symbol rhsSymbol = rhs.next(); + curPosition++; + Class> symbolClass = symbolValueClasses.get(rhsSymbol); + if (symbolClass != null + || (rhsSymbol instanceof AuxiliaryLHS4SemanticShiftAction + && ((AuxiliaryLHS4SemanticShiftAction) rhsSymbol).symbolValueType != Void.TYPE) + || Objects.equals(SpecialTerminals.Error, rhsSymbol)) + throw new IllegalSpecException( + "The action occurs too late or it's method requires too less parameters: " + + rhsSymbols.toString()); + } + } + } + + public static Class resolvePrimitiveTypes(Class clazz) { + + // primitive Type can occur if you use scala for the specification, + // so first wrap the primitive types and then go on with checking + if (clazz.isPrimitive()) { + Class c = null; + + if (clazz.isAssignableFrom(Integer.TYPE)) { + c = Integer.class; + } else if (clazz.isAssignableFrom(Boolean.TYPE)) { + c = Boolean.class; + } else if (clazz.isAssignableFrom(Character.TYPE)) { + c = Character.class; + } else if (clazz.isAssignableFrom(Byte.TYPE)) { + c = Byte.class; + } else if (clazz.isAssignableFrom(Short.TYPE)) { + c = Short.class; + } else if (clazz.isAssignableFrom(Long.TYPE)) { + c = Long.class; + } else if (clazz.isAssignableFrom(Float.TYPE)) { + c = Float.class; + } else if (clazz.isAssignableFrom(Double.TYPE)) { + c = Double.class; } - // rhsSymbol is neither value-binded, an aux NT nor an Error => It has no meaning at all (for example Epsilon) - } - } - //given position must be at least as high as the computed minimal position - if (curPosition < position) - { - while(curPosition < position) - { - if (!rhs.hasNext()) - throw new IllegalSpecException("Parameters do not match for an action for the RHS: " + rhsSymbols.toString()); - Symbol rhsSymbol = rhs.next(); - curPosition++; - Class> symbolClass = symbolValueClasses.get(rhsSymbol); - if ( - symbolClass!=null || - ( - rhsSymbol instanceof AuxiliaryLHS4SemanticShiftAction && - ((AuxiliaryLHS4SemanticShiftAction)rhsSymbol).symbolValueType!=Void.TYPE - ) || - SpecialTerminals.Error.equals(rhsSymbol) - ) - throw new IllegalSpecException("The action occurs too late or it's method requires too less parameters: " + rhsSymbols.toString()); - } - } - } - - public static Class resolvePrimitiveTypes(Class clazz) { - - // primitive Type can occur if you use scala for the specification, - // so first wrap the primitive types and then go on with checking - if (clazz.isPrimitive()) { - Class c = null; - - if(clazz.isAssignableFrom(Integer.TYPE)) { - c = Integer.class; - } else if(clazz.isAssignableFrom(Boolean.TYPE)) { - c = Boolean.class; - } else if(clazz.isAssignableFrom(Character.TYPE)) { - c = Character.class; - } else if(clazz.isAssignableFrom(Byte.TYPE)) { - c = Byte.class; - } else if(clazz.isAssignableFrom(Short.TYPE)) { - c = Short.class; - } else if(clazz.isAssignableFrom(Long.TYPE)) { - c = Long.class; - } else if(clazz.isAssignableFrom(Float.TYPE)) { - c = Float.class; - } else if(clazz.isAssignableFrom(Double.TYPE)) { - c = Double.class; - } - - return c; - } - - return clazz; - } - - /** - * Instantiates enumeration object at runtime. - * @author Stefan Dangl - **/ - public static AuxiliaryLHS4SemanticShiftAction createAuxLHS4SemanticShiftAction( String name ) - { - AuxiliaryLHS4SemanticShiftAction obj = null; + + return c; + } + + return clazz; + } + + /** + * Instantiates enumeration object at runtime. + * + * @author Stefan Dangl + */ + public static AuxiliaryLHS4SemanticShiftAction createAuxLHS4SemanticShiftAction(String name) { + AuxiliaryLHS4SemanticShiftAction obj = null; Exception why = null; - + /** - * Retrieve the constructor of the super-enum's class. - * As enumerations shall not be created during runtime according - * to the designers of Java, a complicated workaround is required. + * Retrieve the constructor of the super-enum's class. As enumerations shall not be created + * during runtime according to the designers of Java, a complicated workaround is required. */ - @SuppressWarnings("unchecked") Constructor con = AuxiliaryLHS4SemanticShiftAction.class.getDeclaredConstructors()[0]; -// System.out.println("Constructor: "+con); + // System.out.println("Constructor: "+con); /** - * Use the constructor accessor of the constructor in order - * to retrieve a newInstance method, which may then be used - * to create an object which extends the enum AuxiliaryLHS4SemanticShiftAction. + * Use the constructor accessor of the constructor in order to retrieve a newInstance method, + * which may then be used to create an object which extends the enum + * AuxiliaryLHS4SemanticShiftAction. */ try { @@ -574,66 +498,58 @@ public static AuxiliaryLHS4SemanticShiftAction createAuxLHS4SemanticShiftAction( // in order to create a constructor-accessor if none has been created yet. Method[] methods = con.getClass().getDeclaredMethods(); for (Method m : methods) { - if (m.getName().equals("acquireConstructorAccessor")) { -// System.out.println("acquireConstructorAccessor() : "+m+" (method)"); + if (Objects.equals(m.getName(), "acquireConstructorAccessor")) { + // System.out.println("acquireConstructorAccessor() : "+m+" (method)"); m.setAccessible(true); m.invoke(con, new Object[0]); } } - + // Retrieve the created constructor-accessor. Field[] fields = con.getClass().getDeclaredFields(); Object ca = null; for (Field f : fields) { - if (f.getName().equals("constructorAccessor")) { -// System.out.println("constructorAccessor : "+f+" (field)"); + if (Objects.equals(f.getName(), "constructorAccessor")) { + // System.out.println("constructorAccessor : "+f+" (field)"); f.setAccessible(true); ca = f.get(con); } } - -// System.out.println("Retrieved constructor accessor successfully :-)"); - - // Invoke the newInstance(Class[]) method of the constructor-accessor - Method m = ca.getClass().getMethod("newInstance", new Class[] { Object[].class }); + + // System.out.println("Retrieved constructor accessor successfully :-)"); + + // Invoke the newInstance(Class[]) method of the constructor-accessor + Method m = ca.getClass().getMethod("newInstance", new Class[] {Object[].class}); m.setAccessible(true); - obj = (AuxiliaryLHS4SemanticShiftAction) m.invoke( - ca, new Object[] { - new Object[] { name, Integer.MAX_VALUE } - } - ); - -// System.out.println(obj.getClass() + " : ( " + obj.name() + " , " + obj.ordinal()+" ) "); + obj = + (AuxiliaryLHS4SemanticShiftAction) + m.invoke(ca, new Object[] {new Object[] {name, Integer.MAX_VALUE}}); + + // System.out.println(obj.getClass() + " : ( " + obj.name() + " , " + obj.ordinal()+" ) + // "); } catch (Exception e) { - why = e; //e.printStackTrace(); + why = e; // e.printStackTrace(); } - - if (obj==null || why!=null) - { - IllegalSpecException rte = new IllegalSpecException( - "Error during creation of artificial enum at runtime : "+why - ); + + if (obj == null || why != null) { + IllegalSpecException rte = + new IllegalSpecException("Error during creation of artificial enum at runtime : " + why); rte.initCause(why); throw rte; } - + return obj; - } + } - /** - * - * Retrieves the return type of a semantic action object. - * - **/ + /** Retrieves the return type of a semantic action object. */ public static Class getReturnTypeOfAction(Action action) { return action.getMethod().getReturnType(); } - public static Object getFieldValueFromObject( Object obj, String field_name ) - { + public static Object getFieldValueFromObject(Object obj, String field_name) { Class c = obj.getClass(); - if (c==null) return null; + if (c == null) return null; Field fld = null; try { fld = c.getDeclaredField(field_name); @@ -643,10 +559,10 @@ public static Object getFieldValueFromObject( Object obj, String field_name ) } catch (NoSuchFieldException e) { return null; } - if (fld==null) return null; + if (fld == null) return null; Object value = null; try { - value = fld.get(obj); + value = fld.get(obj); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (SecurityException e) { diff --git a/testsuite/testcases/src/test/cases/cupsym/.gitignore b/testsuite/testcases/src/test/cases/cupsym/.gitignore new file mode 100644 index 000000000..a073994d4 --- /dev/null +++ b/testsuite/testcases/src/test/cases/cupsym/.gitignore @@ -0,0 +1 @@ +Cupsym.java diff --git a/testsuite/testcases/src/test/cases/dot-newline/.gitignore b/testsuite/testcases/src/test/cases/dot-newline/.gitignore new file mode 100644 index 000000000..35238060e --- /dev/null +++ b/testsuite/testcases/src/test/cases/dot-newline/.gitignore @@ -0,0 +1 @@ +Dotnewline.java diff --git a/testsuite/testcases/src/test/cases/empty-match/.gitignore b/testsuite/testcases/src/test/cases/empty-match/.gitignore new file mode 100644 index 000000000..27d153569 --- /dev/null +++ b/testsuite/testcases/src/test/cases/empty-match/.gitignore @@ -0,0 +1 @@ +*.java diff --git a/testsuite/testcases/src/test/cases/eof-min/.gitignore b/testsuite/testcases/src/test/cases/eof-min/.gitignore new file mode 100644 index 000000000..701dd2f9a --- /dev/null +++ b/testsuite/testcases/src/test/cases/eof-min/.gitignore @@ -0,0 +1 @@ +Eofmin.java diff --git a/testsuite/testcases/src/test/cases/eofclose/.gitignore b/testsuite/testcases/src/test/cases/eofclose/.gitignore new file mode 100644 index 000000000..6fcded67e --- /dev/null +++ b/testsuite/testcases/src/test/cases/eofclose/.gitignore @@ -0,0 +1 @@ +Eofclose.java diff --git a/testsuite/testcases/src/test/cases/eofclose2/.gitignore b/testsuite/testcases/src/test/cases/eofclose2/.gitignore new file mode 100644 index 000000000..9de02120c --- /dev/null +++ b/testsuite/testcases/src/test/cases/eofclose2/.gitignore @@ -0,0 +1 @@ +Eofclose2.java diff --git a/testsuite/testcases/src/test/cases/eol-comment/.gitignore b/testsuite/testcases/src/test/cases/eol-comment/.gitignore new file mode 100644 index 000000000..45b28e7cc --- /dev/null +++ b/testsuite/testcases/src/test/cases/eol-comment/.gitignore @@ -0,0 +1 @@ +Eolcomment.java diff --git a/testsuite/testcases/src/test/cases/eol-look/.gitignore b/testsuite/testcases/src/test/cases/eol-look/.gitignore new file mode 100644 index 000000000..5e5c4a040 --- /dev/null +++ b/testsuite/testcases/src/test/cases/eol-look/.gitignore @@ -0,0 +1 @@ +Eol.java diff --git a/testsuite/testcases/src/test/cases/filename/.gitignore b/testsuite/testcases/src/test/cases/filename/.gitignore new file mode 100644 index 000000000..ee8c8e7c1 --- /dev/null +++ b/testsuite/testcases/src/test/cases/filename/.gitignore @@ -0,0 +1,2 @@ +# Generated file +DangerousFileName.java diff --git a/testsuite/testcases/src/test/cases/filename/filename.test b/testsuite/testcases/src/test/cases/filename/filename.test new file mode 100644 index 000000000..485234a60 --- /dev/null +++ b/testsuite/testcases/src/test/cases/filename/filename.test @@ -0,0 +1,15 @@ +name: filename\u000AFILE_NAMES_MUST_BE_ESCAPED\u000A + +# The generated java file is not the test name +javac-files: DangerousFileName.java + +description: +Tests that the emitter emits valid Java code when the file name is dangerous. +If unescaped, the header would be + // source: path/to/filename\u000AFILE_NAMES_MUST_BE_ESCAPED.flex +Which is strictly equivalent to: + // source: path/to/filename + FILE_NAMES_MUST_BE_ESCAPED + .flex +which doesn't compile. +In particular, on Windows, backslash is use as a path separator, so this problem would be likely. diff --git "a/testsuite/testcases/src/test/cases/filename/filename\\u000AFILE_NAMES_MUST_BE_ESCAPED\\u000A.flex" "b/testsuite/testcases/src/test/cases/filename/filename\\u000AFILE_NAMES_MUST_BE_ESCAPED\\u000A.flex" new file mode 100644 index 000000000..35bd641fe --- /dev/null +++ "b/testsuite/testcases/src/test/cases/filename/filename\\u000AFILE_NAMES_MUST_BE_ESCAPED\\u000A.flex" @@ -0,0 +1,11 @@ + +%% + +%public +%class DangerousFileName + +%int + +%% + +[^] { /* no action */ } diff --git a/testsuite/testcases/src/test/cases/fixed-look/.gitignore b/testsuite/testcases/src/test/cases/fixed-look/.gitignore new file mode 100644 index 000000000..a315cb2bc --- /dev/null +++ b/testsuite/testcases/src/test/cases/fixed-look/.gitignore @@ -0,0 +1 @@ +Fixedlook.java diff --git a/testsuite/testcases/src/test/cases/generics/.gitignore b/testsuite/testcases/src/test/cases/generics/.gitignore new file mode 100644 index 000000000..76800892c --- /dev/null +++ b/testsuite/testcases/src/test/cases/generics/.gitignore @@ -0,0 +1 @@ +Generics*.java diff --git a/testsuite/testcases/src/test/cases/genlook/.gitignore b/testsuite/testcases/src/test/cases/genlook/.gitignore new file mode 100644 index 000000000..6ba83b8bc --- /dev/null +++ b/testsuite/testcases/src/test/cases/genlook/.gitignore @@ -0,0 +1 @@ +Genlook.java diff --git a/testsuite/testcases/src/test/cases/genlook2/.gitignore b/testsuite/testcases/src/test/cases/genlook2/.gitignore new file mode 100644 index 000000000..3b6d7bfe6 --- /dev/null +++ b/testsuite/testcases/src/test/cases/genlook2/.gitignore @@ -0,0 +1 @@ +Genlook2.java diff --git a/testsuite/testcases/src/test/cases/include-in-rules/.gitignore b/testsuite/testcases/src/test/cases/include-in-rules/.gitignore new file mode 100644 index 000000000..e3482b663 --- /dev/null +++ b/testsuite/testcases/src/test/cases/include-in-rules/.gitignore @@ -0,0 +1 @@ +IncludeInRules.java diff --git a/testsuite/testcases/src/test/cases/include/.gitignore b/testsuite/testcases/src/test/cases/include/.gitignore new file mode 100644 index 000000000..013b845e0 --- /dev/null +++ b/testsuite/testcases/src/test/cases/include/.gitignore @@ -0,0 +1 @@ +Include.java diff --git a/testsuite/testcases/src/test/cases/include2/.gitignore b/testsuite/testcases/src/test/cases/include2/.gitignore new file mode 100644 index 000000000..f48b1993c --- /dev/null +++ b/testsuite/testcases/src/test/cases/include2/.gitignore @@ -0,0 +1 @@ +Include2.java diff --git a/testsuite/testcases/src/test/cases/initthrow-eol/.gitignore b/testsuite/testcases/src/test/cases/initthrow-eol/.gitignore new file mode 100644 index 000000000..38a58a424 --- /dev/null +++ b/testsuite/testcases/src/test/cases/initthrow-eol/.gitignore @@ -0,0 +1 @@ +Initthrow.java diff --git a/testsuite/testcases/src/test/cases/java/.gitignore b/testsuite/testcases/src/test/cases/java/.gitignore new file mode 100644 index 000000000..6bee26269 --- /dev/null +++ b/testsuite/testcases/src/test/cases/java/.gitignore @@ -0,0 +1 @@ +Java.java diff --git a/testsuite/testcases/src/test/cases/line-cont/.gitignore b/testsuite/testcases/src/test/cases/line-cont/.gitignore new file mode 100644 index 000000000..bae521e23 --- /dev/null +++ b/testsuite/testcases/src/test/cases/line-cont/.gitignore @@ -0,0 +1 @@ +Yylex.java diff --git a/testsuite/testcases/src/test/cases/line-cont/line.test b/testsuite/testcases/src/test/cases/line-cont/line.test index 23a18f147..5de32379c 100644 --- a/testsuite/testcases/src/test/cases/line-cont/line.test +++ b/testsuite/testcases/src/test/cases/line-cont/line.test @@ -1,3 +1,5 @@ name: line jflex: --nobak --dump + +javac-files: Yylex.java diff --git a/testsuite/testcases/src/test/cases/look-macro/.gitignore b/testsuite/testcases/src/test/cases/look-macro/.gitignore new file mode 100644 index 000000000..b623319fa --- /dev/null +++ b/testsuite/testcases/src/test/cases/look-macro/.gitignore @@ -0,0 +1 @@ +Lookmacro.java diff --git a/testsuite/testcases/src/test/cases/look/.gitignore b/testsuite/testcases/src/test/cases/look/.gitignore new file mode 100644 index 000000000..c38ffe732 --- /dev/null +++ b/testsuite/testcases/src/test/cases/look/.gitignore @@ -0,0 +1 @@ +Look.java diff --git a/testsuite/testcases/src/test/cases/macro-exp/.gitignore b/testsuite/testcases/src/test/cases/macro-exp/.gitignore new file mode 100644 index 000000000..a9dc49fa1 --- /dev/null +++ b/testsuite/testcases/src/test/cases/macro-exp/.gitignore @@ -0,0 +1 @@ +Macro.java diff --git a/testsuite/testcases/src/test/cases/manual-ex/.gitignore b/testsuite/testcases/src/test/cases/manual-ex/.gitignore new file mode 100644 index 000000000..c9c7aa513 --- /dev/null +++ b/testsuite/testcases/src/test/cases/manual-ex/.gitignore @@ -0,0 +1 @@ +Lexer.java diff --git a/testsuite/testcases/src/test/cases/manual-ex/manual.test b/testsuite/testcases/src/test/cases/manual-ex/manual.test index 4e6be1399..f8e5bbe22 100644 --- a/testsuite/testcases/src/test/cases/manual-ex/manual.test +++ b/testsuite/testcases/src/test/cases/manual-ex/manual.test @@ -4,3 +4,4 @@ the introductory example from the manual jflex: --nobak +javac-files: Lexer.java sym.java diff --git a/testsuite/testcases/src/test/cases/nevermatch/.gitignore b/testsuite/testcases/src/test/cases/nevermatch/.gitignore new file mode 100644 index 000000000..e350cb576 --- /dev/null +++ b/testsuite/testcases/src/test/cases/nevermatch/.gitignore @@ -0,0 +1 @@ +Never.java diff --git a/testsuite/testcases/src/test/cases/nevermatch/never.test b/testsuite/testcases/src/test/cases/nevermatch/never.test index 061446819..f6afd6d1d 100755 --- a/testsuite/testcases/src/test/cases/nevermatch/never.test +++ b/testsuite/testcases/src/test/cases/nevermatch/never.test @@ -4,6 +4,6 @@ test nevermatch warning jflex: -q -javac-extra-files: Never.java +javac-files: Never.java javac-fail: true diff --git a/testsuite/testcases/src/test/cases/no-unused/.gitignore b/testsuite/testcases/src/test/cases/no-unused/.gitignore new file mode 100644 index 000000000..d1006cef2 --- /dev/null +++ b/testsuite/testcases/src/test/cases/no-unused/.gitignore @@ -0,0 +1 @@ +NoUnused.java diff --git a/testsuite/testcases/src/test/cases/no-unused/no-unused.test b/testsuite/testcases/src/test/cases/no-unused/no-unused.test index 569cbec40..4515003ca 100644 --- a/testsuite/testcases/src/test/cases/no-unused/no-unused.test +++ b/testsuite/testcases/src/test/cases/no-unused/no-unused.test @@ -4,3 +4,5 @@ description: testing the --no-warn-unused option jflex: -v --nobak --no-warn-unused + +javac-files: NoUnused.java diff --git a/testsuite/testcases/src/test/cases/simple/.gitignore b/testsuite/testcases/src/test/cases/simple/.gitignore new file mode 100644 index 000000000..848b6e6b5 --- /dev/null +++ b/testsuite/testcases/src/test/cases/simple/.gitignore @@ -0,0 +1 @@ +Simple.java diff --git a/testsuite/testcases/src/test/cases/six-digit-unicode-escape/.gitignore b/testsuite/testcases/src/test/cases/six-digit-unicode-escape/.gitignore new file mode 100644 index 000000000..6ef76be35 --- /dev/null +++ b/testsuite/testcases/src/test/cases/six-digit-unicode-escape/.gitignore @@ -0,0 +1 @@ +SixDigitUnicodeEscape*.java diff --git a/testsuite/testcases/src/test/cases/testloader/.gitignore b/testsuite/testcases/src/test/cases/testloader/.gitignore new file mode 100644 index 000000000..cf8925879 --- /dev/null +++ b/testsuite/testcases/src/test/cases/testloader/.gitignore @@ -0,0 +1 @@ +Testloader.java diff --git a/testsuite/testcases/src/test/cases/unicode-age/.gitignore b/testsuite/testcases/src/test/cases/unicode-age/.gitignore new file mode 100644 index 000000000..8b19db1dc --- /dev/null +++ b/testsuite/testcases/src/test/cases/unicode-age/.gitignore @@ -0,0 +1 @@ +UnicodeAge_*.java diff --git a/testsuite/testcases/src/test/cases/unicode-blocks/.gitignore b/testsuite/testcases/src/test/cases/unicode-blocks/.gitignore new file mode 100644 index 000000000..fb56b78aa --- /dev/null +++ b/testsuite/testcases/src/test/cases/unicode-blocks/.gitignore @@ -0,0 +1 @@ +Unicode*.java diff --git a/testsuite/testcases/src/test/cases/unicode-caseless/.gitignore b/testsuite/testcases/src/test/cases/unicode-caseless/.gitignore new file mode 100644 index 000000000..e480ef9a9 --- /dev/null +++ b/testsuite/testcases/src/test/cases/unicode-caseless/.gitignore @@ -0,0 +1 @@ +UnicodeCaseless*.java diff --git a/testsuite/testcases/src/test/cases/unicode-codepoint-escapes/.gitignore b/testsuite/testcases/src/test/cases/unicode-codepoint-escapes/.gitignore new file mode 100644 index 000000000..705dd54a7 --- /dev/null +++ b/testsuite/testcases/src/test/cases/unicode-codepoint-escapes/.gitignore @@ -0,0 +1 @@ +UnicodeCodePointEscapes*.java diff --git a/testsuite/testcases/src/test/cases/unicode-compatibility-properties/.gitignore b/testsuite/testcases/src/test/cases/unicode-compatibility-properties/.gitignore new file mode 100644 index 000000000..60acb12c9 --- /dev/null +++ b/testsuite/testcases/src/test/cases/unicode-compatibility-properties/.gitignore @@ -0,0 +1 @@ +UnicodeCompatibilityProperties*.java diff --git a/testsuite/testcases/src/test/cases/unicode-derived-core-properties/.gitignore b/testsuite/testcases/src/test/cases/unicode-derived-core-properties/.gitignore new file mode 100644 index 000000000..58583a4a8 --- /dev/null +++ b/testsuite/testcases/src/test/cases/unicode-derived-core-properties/.gitignore @@ -0,0 +1 @@ +UnicodeDerivedCoreProperties*.java diff --git a/testsuite/testcases/src/test/cases/unicode-digit/.gitignore b/testsuite/testcases/src/test/cases/unicode-digit/.gitignore new file mode 100644 index 000000000..a2b8146e0 --- /dev/null +++ b/testsuite/testcases/src/test/cases/unicode-digit/.gitignore @@ -0,0 +1 @@ +UnicodeDecimalDigit*.java diff --git a/testsuite/testcases/src/test/cases/unicode-general-category/.gitignore b/testsuite/testcases/src/test/cases/unicode-general-category/.gitignore new file mode 100644 index 000000000..03e778cf3 --- /dev/null +++ b/testsuite/testcases/src/test/cases/unicode-general-category/.gitignore @@ -0,0 +1,2 @@ +UnicodeGeneralCategory*.java +UnicodeNotGeneralCategory*.java diff --git a/testsuite/testcases/src/test/cases/unicode-grapheme-break/.gitignore b/testsuite/testcases/src/test/cases/unicode-grapheme-break/.gitignore new file mode 100644 index 000000000..0262d7faf --- /dev/null +++ b/testsuite/testcases/src/test/cases/unicode-grapheme-break/.gitignore @@ -0,0 +1 @@ +UnicodeGrapheme*Break*.java diff --git a/testsuite/testcases/src/test/cases/unicode-letter/.gitignore b/testsuite/testcases/src/test/cases/unicode-letter/.gitignore new file mode 100644 index 000000000..754347ca8 --- /dev/null +++ b/testsuite/testcases/src/test/cases/unicode-letter/.gitignore @@ -0,0 +1 @@ +UnicodeLetter*.java diff --git a/testsuite/testcases/src/test/cases/unicode-line-break/.gitignore b/testsuite/testcases/src/test/cases/unicode-line-break/.gitignore new file mode 100644 index 000000000..5e77ab6b0 --- /dev/null +++ b/testsuite/testcases/src/test/cases/unicode-line-break/.gitignore @@ -0,0 +1 @@ +UnicodeLineBreak*.java diff --git a/testsuite/testcases/src/test/cases/unicode-lowercase/.gitignore b/testsuite/testcases/src/test/cases/unicode-lowercase/.gitignore new file mode 100644 index 000000000..769c196cc --- /dev/null +++ b/testsuite/testcases/src/test/cases/unicode-lowercase/.gitignore @@ -0,0 +1 @@ +UnicodeLowercase*.java diff --git a/testsuite/testcases/src/test/cases/unicode-misc-properties/.gitignore b/testsuite/testcases/src/test/cases/unicode-misc-properties/.gitignore new file mode 100644 index 000000000..63fa1c6f2 --- /dev/null +++ b/testsuite/testcases/src/test/cases/unicode-misc-properties/.gitignore @@ -0,0 +1 @@ +UnicodeMisc*.java diff --git a/testsuite/testcases/src/test/cases/unicode-proplist/.gitignore b/testsuite/testcases/src/test/cases/unicode-proplist/.gitignore new file mode 100644 index 000000000..fb88b12bb --- /dev/null +++ b/testsuite/testcases/src/test/cases/unicode-proplist/.gitignore @@ -0,0 +1 @@ +UnicodePropList*.java diff --git a/testsuite/testcases/src/test/cases/unicode-scripts/.gitignore b/testsuite/testcases/src/test/cases/unicode-scripts/.gitignore new file mode 100644 index 000000000..fb56b78aa --- /dev/null +++ b/testsuite/testcases/src/test/cases/unicode-scripts/.gitignore @@ -0,0 +1 @@ +Unicode*.java diff --git a/testsuite/testcases/src/test/cases/unicode-sentence-break/.gitignore b/testsuite/testcases/src/test/cases/unicode-sentence-break/.gitignore new file mode 100644 index 000000000..1ef80583f --- /dev/null +++ b/testsuite/testcases/src/test/cases/unicode-sentence-break/.gitignore @@ -0,0 +1 @@ +UnicodeSentenceBreak*.java diff --git a/testsuite/testcases/src/test/cases/unicode-space/.gitignore b/testsuite/testcases/src/test/cases/unicode-space/.gitignore new file mode 100644 index 000000000..d245741f1 --- /dev/null +++ b/testsuite/testcases/src/test/cases/unicode-space/.gitignore @@ -0,0 +1 @@ +UnicodeWhiteSpace*.java diff --git a/testsuite/testcases/src/test/cases/unicode-uppercase/.gitignore b/testsuite/testcases/src/test/cases/unicode-uppercase/.gitignore new file mode 100644 index 000000000..8e453b88f --- /dev/null +++ b/testsuite/testcases/src/test/cases/unicode-uppercase/.gitignore @@ -0,0 +1 @@ +UnicodeUppercase*.java diff --git a/testsuite/testcases/src/test/cases/unicode-word-break/.gitignore b/testsuite/testcases/src/test/cases/unicode-word-break/.gitignore new file mode 100644 index 000000000..2f7e87385 --- /dev/null +++ b/testsuite/testcases/src/test/cases/unicode-word-break/.gitignore @@ -0,0 +1 @@ +UnicodeWordBreak*.java diff --git a/testsuite/testcases/src/test/cases/unicode-word/.gitignore b/testsuite/testcases/src/test/cases/unicode-word/.gitignore new file mode 100644 index 000000000..64371d5b4 --- /dev/null +++ b/testsuite/testcases/src/test/cases/unicode-word/.gitignore @@ -0,0 +1 @@ +UnicodeWord*.java diff --git a/testsuite/testcases/src/test/cases/unused/.gitignore b/testsuite/testcases/src/test/cases/unused/.gitignore new file mode 100644 index 000000000..75075099f --- /dev/null +++ b/testsuite/testcases/src/test/cases/unused/.gitignore @@ -0,0 +1 @@ +Unused.java diff --git a/third_party/BUILD b/third_party/BUILD new file mode 100644 index 000000000..5d88f562a --- /dev/null +++ b/third_party/BUILD @@ -0,0 +1 @@ +# Defines the root //third_party package diff --git a/third_party/README.md b/third_party/README.md new file mode 100644 index 000000000..d4e3f63b3 --- /dev/null +++ b/third_party/README.md @@ -0,0 +1,9 @@ +# Bazel third-party packages + +This is *not* an example. +It contains BUILD aliases for the [Bazel build system][bazel]. + +To read how to use JFlex on your Bazel project, please read +[de/jflex/README.md](de/jflex/README.md) + +[bazel]: https://bazel.build/ diff --git a/third_party/com/google/guava/BUILD b/third_party/com/google/guava/BUILD new file mode 100644 index 000000000..04cc22169 --- /dev/null +++ b/third_party/com/google/guava/BUILD @@ -0,0 +1,11 @@ +# Google Guava +# Google core libraries for Java +# https://github.com/google/guava + +licenses(["notice"]) # Apache License 2.0 + +java_library( + name = "guava", + visibility = ["//visibility:public"], + exports = ["@com_google_guava_guava//jar"], +) diff --git a/third_party/com/google/truth/BUILD b/third_party/com/google/truth/BUILD new file mode 100644 index 000000000..a04059a39 --- /dev/null +++ b/third_party/com/google/truth/BUILD @@ -0,0 +1,16 @@ +# Google truth +# Fluent assertions for Java tests +# http://google.github.io/truth/ + +licenses(["notice"]) # Apache License 2.0 + +java_library( + name = "truth", + visibility = ["//visibility:public"], + exports = [ + "@com_google_truth_truth//jar", + # TODO(regisd) This should be a runtime_deps + # But in tht case Optional is not found + "//third_party/com/google/guava", + ], +) diff --git a/third_party/de/jflex/.gitignore b/third_party/de/jflex/.gitignore new file mode 100644 index 000000000..e69de29bb diff --git a/third_party/deps.bzl b/third_party/deps.bzl new file mode 100644 index 000000000..f17259289 --- /dev/null +++ b/third_party/deps.bzl @@ -0,0 +1,23 @@ +# Please keep deps in alphabetical order +def third_party_deps(): + native.maven_jar( + name = "org_apache_ant_ant", + artifact = "org.apache.ant:ant:1.7.0", + repository = "https://jcenter.bintray.com/", + sha1 = "9746af1a485e50cf18dcb232489032a847067066", + ) + native.maven_jar( + name = "com_google_guava_guava", + artifact = "com.google.guava:guava:jar:26.0-jre", + repository = "http://jcenter.bintray.com/", + ) + native.maven_jar( + name = "com_google_truth_truth", + artifact = "com.google.truth:truth:0.36", + repository = "http://jcenter.bintray.com/", + ) + native.maven_jar( + name = "junit_junit", + artifact = "junit:junit:jar:4.12", + repository = "http://jcenter.bintray.com/", + ) diff --git a/third_party/junit/BUILD b/third_party/junit/BUILD new file mode 100644 index 000000000..20ebdeb2c --- /dev/null +++ b/third_party/junit/BUILD @@ -0,0 +1,9 @@ +# https://junit.org/junit4 + +licenses(["notice"]) # Eclipse Public License 1.0 + +java_library( + name = "junit", + visibility = ["//visibility:public"], + exports = ["@junit_junit//jar"], +) diff --git a/third_party/org/apache/ant/BUILD b/third_party/org/apache/ant/BUILD new file mode 100644 index 000000000..72e6f4c7f --- /dev/null +++ b/third_party/org/apache/ant/BUILD @@ -0,0 +1,8 @@ +package(default_visibility = ["//visibility:public"]) + +licenses(["notice"]) + +alias( + name = "ant", + actual = "@org_apache_ant_ant//jar", +)