diff --git a/README.md b/README.md index c307662d8..43fa19fe9 100644 --- a/README.md +++ b/README.md @@ -23,13 +23,13 @@ Error recovery. ## Project structure ``` -├── solver -- base ucfs logic -├── benchmarks -- comparison with antlr4 -├── generator -- parser and ast node classes generator -└── test-shared -- test cases +├── solver -- base ucfs logic +├── benchmarks -- comparison with antlr4 +├── generator -- parser and ast node classes generator +└── test-shared -- test cases └── src - └── test - └── resources -- grammars' description and inputs + └── main.kotlin.org.ucfs.grammars -- grammars' description + └── test.resources.grammars -- grammars' inputs ``` ## Core Algorithm diff --git a/benchmarks/README.md b/benchmarks/README.md new file mode 100644 index 000000000..99be211f5 --- /dev/null +++ b/benchmarks/README.md @@ -0,0 +1,50 @@ +# UCFSBenchmarks + +## Prerequisites + +```text +(1) Gradle (version >= 7.2) +(2) Antlr V4 +(3) Jflex +``` + +## Build project + +#### Step 1. Clone repository + +`git clone https://github.com/cyb3r-b4stard/UCFSBenchmarks.git` + +or + +`git@github.com:cyb3r-b4stard/UCFSBenchmarks.git` + +or + +`gh repo clone cyb3r-b4stard/UCFSBenchmarks` + +#### Step 2. Go to the project folder + +`cd UCFSBenchmarks` + +#### Step 3. Install dependencies + +`./ucfs_install.sh` + +#### Step 4. Build project + +`./ucfs_build.sh` + +## Execute Benchmarks + +#### Step 1. Run benchmarks + +`./ucfs_bench.sh` + +## Logging + +Logs are stored in build/libs/Benchmarks.log + +## Results + +After executing all benchmarks, results file will be located in the `/build` folder +under name `results.csv`. That file shall be transfered back to Ivan Lomikovskiy \ No newline at end of file diff --git a/benchmarks/build.gradle.kts b/benchmarks/build.gradle.kts index 656654148..18b8869ad 100644 --- a/benchmarks/build.gradle.kts +++ b/benchmarks/build.gradle.kts @@ -17,6 +17,12 @@ dependencies { jmhImplementation("org.openjdk.jmh:jmh-core:1.36") jmhImplementation("org.openjdk.jmh:jmh-generator-annprocess:1.36") jmhImplementation("org.openjdk.jmh:jmh-generator-bytecode:1.36") + //for iguana + implementation("io.usethesource:capsule:0.6.3") + implementation("info.picocli:picocli:4.7.0") + implementation("com.google.guava:guava-testlib:23.0") + implementation("com.fasterxml.jackson.core:jackson-core:2.14.0") + implementation("com.fasterxml.jackson.core:jackson-databind:2.14.0") } kotlin { jvmToolchain(11) } diff --git a/benchmarks/gradle.properties b/benchmarks/gradle.properties new file mode 100644 index 000000000..2de43ab72 --- /dev/null +++ b/benchmarks/gradle.properties @@ -0,0 +1,3 @@ +kotlin.code.style=official +org.gradle.daemon=true +org.gradle.jvmargs=-Xmx4096m -Xss4m -XX:+UseG1GC \ No newline at end of file diff --git a/benchmarks/gradle/wrapper/gradle-wrapper.properties b/benchmarks/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000..06febab41 --- /dev/null +++ b/benchmarks/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists \ No newline at end of file diff --git a/benchmarks/gradlew b/benchmarks/gradlew new file mode 100755 index 000000000..1b6c78733 --- /dev/null +++ b/benchmarks/gradlew @@ -0,0 +1,234 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed 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 +# +# https://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. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +APP_NAME="Gradle" +APP_BASE_NAME=${0##*/} + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/benchmarks/gradlew.bat b/benchmarks/gradlew.bat new file mode 100644 index 000000000..107acd32c --- /dev/null +++ b/benchmarks/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/benchmarks/src/jmh/kotlin/Benchmarks.kt b/benchmarks/src/jmh/kotlin/Benchmarks.kt new file mode 100644 index 000000000..443115192 --- /dev/null +++ b/benchmarks/src/jmh/kotlin/Benchmarks.kt @@ -0,0 +1,45 @@ +import org.ucfs.input.* +import java.io.StringReader + +fun getResultPath( + pathToOutput: String, + inputName: String, + grammarMode: String, + grammarName: String, + sppfMode: String, +): String { + return pathToOutput + (if (pathToOutput.endsWith("/")) "" else "/") + "${inputName}_${grammarMode}_${grammarName}_${sppfMode}.csv" +} + +fun getTokenStream(input: String): LinearInput { + val inputGraph = LinearInput() + val lexer = JavaLexer(StringReader(input)) + var token: JavaToken + var vertexId = 1 + + inputGraph.addVertex(vertexId) + inputGraph.addStartVertex(vertexId) + + while (true) { + token = lexer.yylex() as JavaToken + if (token == JavaToken.EOF) break + inputGraph.addEdge(vertexId, LinearInputLabel(Terminal(token)), ++vertexId) + } + + return inputGraph +} + +fun getCharStream(input: String): LinearInput { + val inputGraph = LinearInput() + var vertexId = 1 + + inputGraph.addVertex(vertexId) + inputGraph.addStartVertex(vertexId) + + for (ch in input) { + inputGraph.addEdge(vertexId, LinearInputLabel(Terminal(ch.toString())), ++vertexId) + inputGraph.addVertex(vertexId) + } + + return inputGraph +} diff --git a/benchmarks/src/jmh/kotlin/Jmh.kt b/benchmarks/src/jmh/kotlin/Jmh.kt new file mode 100644 index 000000000..c52a2cb1d --- /dev/null +++ b/benchmarks/src/jmh/kotlin/Jmh.kt @@ -0,0 +1,74 @@ +//package jmh.kotlin +// +//import org.antlr.Java8Lexer +//import org.antlr.Java8Parser +//import org.antlr.v4.runtime.CharStreams +//import org.antlr.v4.runtime.CommonTokenStream +//import org.openjdk.jmh.annotations.* +//import org.openjdk.jmh.infra.Blackhole +//import org.openjdk.jmh.infra.Control +//import org.openjdk.jmh.results.format.ResultFormatType +//import org.openjdk.jmh.runner.Runner +//import org.openjdk.jmh.runner.options.OptionsBuilder +//import org.srcgll.Gll +//import org.srcgll.ReachabilityMode +//import org.srcgll.RecoveryMode +//import org.srcgll.lexer.JavaGrammar +//import org.srcgll.rsm.RsmState +//import java.io.File +//import java.util.concurrent.TimeUnit +// +////val pathToInput = "/home/hollowcoder/Programming/SRC/UCFS/src/jmh/resources/junit4SourcesProcessedErrorFree/" +// +//@State(Scope.Benchmark) +//open class JmhBenchmark { +// +// @State(Scope.Thread) +// open class AntlrState{ +// lateinit var file: File +// +// companion object { +// lateinit var sources: Iterator +// } +// +// @Setup(Level.Trial) +// fun prepare() { +// sources = File(pathToInput).walk().filter { it.isFile }.iterator() +// file = sources.next() +// } +// } +// +// @State(Scope.Thread) +// open class GllState{ +// lateinit var file: File +// +// lateinit var startStateJavaTokenized: RsmState +// +// companion object { +// lateinit var sources: Iterator +// } +// +// @Setup(Level.Trial) +// fun prepare() { +// startStateJavaTokenized = JavaGrammar().getRsm() +// sources = File(pathToInput).walk().filter { it.isFile }.iterator() +// file = sources.next() +// } +// } +// +// @Benchmark +// @OutputTimeUnit(TimeUnit.NANOSECONDS) +// fun measureAntlr(stateObject: AntlrState, blackhole: Blackhole) { +// val antlrParser = Java8Parser(CommonTokenStream(Java8Lexer(CharStreams.fromString(stateObject.file.readText())))) +// blackhole.consume(antlrParser.compilationUnit()) +// } +// +// @Benchmark +// @OutputTimeUnit(TimeUnit.NANOSECONDS) +// fun measureGll(stateObject: GllState, blackhole: Blackhole) { +// val inputGraph = getTokenStream(stateObject.file.readText()) +// val gll = Gll(stateObject.startStateJavaTokenized, inputGraph, recovery = RecoveryMode.ON, reachability = ReachabilityMode.REACHABILITY) +// +// blackhole.consume(gll.parse()) +// } +//} diff --git a/benchmarks/src/jmh/kotlin/JmhBenchmark.kt b/benchmarks/src/jmh/kotlin/JmhBenchmark.kt deleted file mode 100644 index 94c49e50f..000000000 --- a/benchmarks/src/jmh/kotlin/JmhBenchmark.kt +++ /dev/null @@ -1,87 +0,0 @@ -package jmh.kotlin - - -import antlr4.Java8Parser -import antlr4.Java8Lexer -import lexers.JavaLexer -import lexers.JavaToken -import grammars.JavaGrammar -import org.antlr.v4.runtime.CharStreams -import org.antlr.v4.runtime.CommonTokenStream -import org.ucfs.parser.Gll -import org.openjdk.jmh.annotations.* -import org.openjdk.jmh.infra.Blackhole -import org.ucfs.input.LinearInputLabel -import org.ucfs.input.RecoveryLinearInput -import org.ucfs.rsm.symbol.Term -import java.io.File -import java.io.StringReader -import java.util.concurrent.TimeUnit - -val pathToInput = "benchmarks/src/jmh/resources/srcFiles/" - -fun getTokenStream(input: String): RecoveryLinearInput { - val inputGraph = RecoveryLinearInput() - val lexer = JavaLexer(StringReader(input)) - var vertexId = 0 - var token: JavaToken - - inputGraph.addStartVertex(vertexId) - inputGraph.addVertex(vertexId) - - while (true) { - token = lexer.yylex() as JavaToken - if (token == JavaToken.EOF) break - println(token.name) - inputGraph.addEdge(vertexId, LinearInputLabel(Term(token)), ++vertexId) - inputGraph.addVertex(vertexId) - } - - return inputGraph -} - -@State(Scope.Benchmark) -@BenchmarkMode(Mode.SingleShotTime) -open class AntlrBenchmark { - - @Param("OneTestCase_processed.java","NotPublicTestCase_processed.java","NoTestsRemainException_processed.java","OldTests_processed.java","TestRule_processed.java","JUnit4ClassRunner_processed.java","CategoryFilterFactoryTest_processed.java","TestClassTest_processed.java","ObjectContractTest_processed.java","LoggingStatement_processed.java","ThrowableMessageMatcher_processed.java","SortingRequest_processed.java","InheritedTestTest_processed.java","SerializableValueDescription_processed.java","JUnit3Builder_processed.java","FrameworkField_processed.java","JavadocTest_processed.java","SynchronizedRunListenerTest_processed.java","Ordering_processed.java","ClassRulesTest_processed.java","ComparisonCriteria_processed.java","TestDescriptionMethodNameTest_processed.java","JUnit4TestAdapterCache_processed.java","RuleContainer_processed.java","InvalidTestClassError_processed.java","JUnit4TestCaseFacade_processed.java","StubbedTheoriesTest_processed.java","FailedConstructionTest_processed.java","ReflectiveThreadMXBean_processed.java","Theory_processed.java","DataPoint_processed.java","Ignore_processed.java","ExpectedExceptionMatcherBuilder_processed.java","BlockJUnit4ClassRunnerWithParameters_processed.java","TestSystem_processed.java","TestWithParametersTest_processed.java","MultiCategoryTest_processed.java","AllMembersSupplier_processed.java","AnnotationsValidator_processed.java","ActiveTestSuite_processed.java","AssertTest_processed.java","RunListener_processed.java","Assume_processed.java","DataPoints_processed.java","TheoryTestUtils_processed.java","AllDefaultPossibilitiesBuilder_processed.java","TestRuleTest_processed.java","AllAssertionTests_processed.java","InvalidOrderingException_processed.java","ResultPrinter_processed.java","AllManipulationTests_processed.java","TextListenerTest_processed.java","Sortable_processed.java","ParameterizedNamesTest_processed.java","ParameterSignature_processed.java","RunnerBuilderStub_processed.java","ValidationTest_processed.java","StubbedTheories_processed.java","SuiteMethodBuilder_processed.java","AllRunnersTests_processed.java","PotentialAssignment_processed.java","StacktracePrintingMatcher_processed.java","Filterable_processed.java","SystemExitTest_processed.java","Filter_processed.java","MainRunner_processed.java","Result_processed.java","TemporaryFolderUsageTest_processed.java","AllTestsTest_processed.java","MultipleFailureException_processed.java","AssertionFailedError_processed.java","ParallelComputer_processed.java","AfterClass_processed.java","UseSuiteAsASuperclassTest_processed.java","ClassLevelMethodsWithIgnoredTestsTest_processed.java","MethodRulesTest_processed.java","Correspondent_processed.java","TypeMatchingBetweenMultiDataPointsMethod_processed.java","ActiveTestTest_processed.java","TestWatchman_processed.java","BadlyFormedClassesTest_processed.java","TestSuite_processed.java","MaxHistory_processed.java","AllParallelTests_processed.java","ComparisonCompactor_processed.java","ParameterSupplier_processed.java","AllClassesTests_processed.java","BlockJUnit4ClassRunnerWithParametersFactory_processed.java","AnnotatedBuilderTest_processed.java","AllExperimentalTests_processed.java","OverrideTestCase_processed.java","TempFolderRuleTest_processed.java","ComparisonFailureTest_processed.java","Parameterized_processed.java","ExpectExceptionTest_processed.java","PrintableResult_processed.java","ReflectiveRuntimeMXBean_processed.java","AllCoreTests_processed.java","ComparisonFailure_processed.java","RunAfters_processed.java","AlphanumericOrdering_processed.java","TestImplementorTest_processed.java","WithParameterSupplier_processed.java","WasRun_processed.java","MultipleFailureExceptionTest_processed.java","RuleChainTest_processed.java","TestListener_processed.java","Statement_processed.java","RepeatedTestTest_processed.java","BlockJUnit4ClassRunner_processed.java","FilterOptionIntegrationTest_processed.java","TestCaseTest_processed.java","ExpectedTest_processed.java","TextRunnerTest_processed.java","EnclosedTest_processed.java","InexactComparisonCriteria_processed.java","OrderWith_processed.java","IMoney_processed.java","UnsuccessfulWithDataPointFields_processed.java","Theories_processed.java","OrderableTest_processed.java","Protectable_processed.java","StacktracePrintingMatcherTest_processed.java","Description_processed.java","BlockJUnit4ClassRunnerTest_processed.java","ParentRunnerTest_processed.java","SuiteTest_processed.java","WithAutoGeneratedDataPoints_processed.java","ExpectException_processed.java","BaseTestRunnerTest_processed.java","TestDescriptionTest_processed.java","SynchronizedRunListener_processed.java","AllParameterizedTests_processed.java","AllModelTests_processed.java","Comparators_processed.java","ThreeTestCases_processed.java","RuleChain_processed.java","Computer_processed.java","TestClass_processed.java","SuiteDescriptionTest_processed.java","MaxCore_processed.java","CustomBlockJUnit4ClassRunnerTest_processed.java","MemoizingRequest_processed.java","ErrorReportingRunnerTest_processed.java","JUnit38ClassRunner_processed.java","TextListener_processed.java","FakeRuntimeMXBean_processed.java","PublicClassValidatorTest_processed.java","Timeout_processed.java","StopwatchTest_processed.java","ConcurrentRunNotifierTest_processed.java","TestCouldNotBeSkippedException_processed.java","Success_processed.java","LoggingMethodRule_processed.java","FilterFactoryParams_processed.java","AssumptionTest_processed.java","WithExtendedParameterSources_processed.java","FilterableTest_processed.java","AllDescriptionTests_processed.java","JUnit4_processed.java","AllRunnerTests_processed.java","SuiteMethodTest_processed.java","SingleMethodTest_processed.java","Describable_processed.java","JUnit4Builder_processed.java","FrameworkFieldTest_processed.java","IgnoreClassTest_processed.java","JUnitCommandLineParseResultTest_processed.java","MatcherTest_processed.java","ThrowableCauseMatcherTest_processed.java","AssumptionViolatedException_processed.java","CategoryValidatorTest_processed.java","ParentRunnerFilteringTest_processed.java","Orderable_processed.java","TestMethodTest_processed.java","ExternalResource_processed.java","ValidateWith_processed.java","AllCategoriesTests_processed.java","ExcludeCategories_processed.java","StoppedByUserException_processed.java","ParameterizedTestMethodTest_processed.java","FilterFactoriesTest_processed.java","RunBefores_processed.java","AllResultsTests_processed.java","ComparisonCompactorTest_processed.java","PrintableResultTest_processed.java","SampleJUnit3Tests_processed.java","ResultTest_processed.java","WithOnlyTestAnnotations_processed.java","Super_processed.java","TestedOn_processed.java","NullBuilder_processed.java","NoTestCases_processed.java","TestMethod_processed.java","Annotatable_processed.java","AssumptionViolatedExceptionTest_processed.java","NoArgTestCaseTest_processed.java","ErrorCollector_processed.java","AllSamplesTests_processed.java","RealSystem_processed.java","MethodSorterTest_processed.java","ForwardCompatibilityPrintingTest_processed.java","Guesser_processed.java","RepeatedTest_processed.java","TestSetup_processed.java","FilterFactory_processed.java","RuleContainerTest_processed.java","CouldNotReadCoreException_processed.java","ErrorReportingRunner_processed.java","FakeThreadMXBean_processed.java","VerifierRuleTest_processed.java","OrderWithValidator_processed.java","SpecificDataPointsSupplierTest_processed.java","WithDataPointMethod_processed.java","MethodValidator_processed.java","MemberValueConsumer_processed.java","ParentRunnerClassLoaderTest_processed.java","AllMethodsTests_processed.java","AllMembersSupplierTest_processed.java","RunRules_processed.java","ListenerTest_processed.java","RequestTest_processed.java","IgnoredBuilder_processed.java","IgnoredClassRunner_processed.java","SuiteMethod_processed.java","JUnitCoreTest_processed.java","TestResult_processed.java","NoGenericTypeParametersValidator_processed.java","RuleMemberValidator_processed.java","InitializationError_processed.java","Failure_processed.java","FilterTest_processed.java","ForwardCompatibilityTest_processed.java","RunnerBuilder_processed.java","AllTheoriesInternalTests_processed.java","IncludeCategories_processed.java","SuccessfulWithDataPointFields_processed.java","MaxStarterTest_processed.java","PublicClassValidator_processed.java","TypeSafeMatcher_processed.java","RuleMemberValidatorTest_processed.java","Stopwatch_processed.java","JUnitCommandLineParseResult_processed.java","RunWithTest_processed.java","TestWithParameters_processed.java","Categories_processed.java","MoneyBag_processed.java","JUnitMatchers_processed.java","FilterRequest_processed.java","ReguessableValue_processed.java","Stub_processed.java","OrderingRequest_processed.java","TimeoutRuleTest_processed.java","EnumSupplier_processed.java","JUnit38SortingTest_processed.java","AllInternalTests_processed.java","FailureList_processed.java","EventCollector_processed.java","MethodRule_processed.java","RunnerScheduler_processed.java","ErrorCollectorTest_processed.java","ArrayComparisonFailureTest_processed.java","InheritedTestCase_processed.java","ManagementFactory_processed.java","FailOnTimeout_processed.java","MethodSorters_processed.java","Category_processed.java","Checks_processed.java","AllJUnit3CompatibilityTests_processed.java","EachTestNotifier_processed.java","CategoryValidator_processed.java","ReflectiveCallable_processed.java","WithUnresolvedGenericTypeVariablesOnTheoryParms_processed.java","ParallelMethodTest_processed.java","TestWatcher_processed.java","TheoriesPerformanceTest_processed.java","TestListenerTest_processed.java","TestWithClassRule_processed.java","Fail_processed.java","ThrowableCauseMatcher_processed.java","DisableOnDebug_processed.java","AnnotationValidatorFactory_processed.java","RunnerSpy_processed.java","ParallelClassTest_processed.java","ParameterSignatureTest_processed.java","FrameworkMember_processed.java","TimeoutTest_processed.java","MethodRoadie_processed.java","OrderWithValidatorTest_processed.java","TemporaryFolder_processed.java","JUnit4TestAdapterTest_processed.java","NameRulesTest_processed.java","ListTest_processed.java","FailOnTimeoutTest_processed.java","ParameterizedAssertionError_processed.java","TestedOnSupplierTest_processed.java","TestName_processed.java","ParametersSuppliedBy_processed.java","Sub_processed.java","AllMaxTests_processed.java","Sorter_processed.java","ExternalResourceRuleTest_processed.java","ThreadsTest_processed.java","Version_processed.java","BaseTestRunner_processed.java","LoggingTestWatcher_processed.java","ExactComparisonCriteria_processed.java","Suite_processed.java","Before_processed.java","FailedBefore_processed.java","MoneyTest_processed.java","ExpectedException_processed.java","ExtensionTest_processed.java","RunNotifierTest_processed.java","OldTestClassAdaptingListenerTest_processed.java","Test_processed.java","AllNotificationTests_processed.java","ResultMatchersTest_processed.java","SampleJUnit4Tests_processed.java","ParentRunner_processed.java","AnnotationTest_processed.java","ThreadMXBean_processed.java","TestCase_processed.java","AnnotationValidator_processed.java","Money_processed.java","Assert_processed.java","DescriptionTest_processed.java","JUnit4TestAdapter_processed.java","WhenNoParametersMatch_processed.java","TestedOnSupplier_processed.java","ParameterizedTestTest_processed.java","FromDataPoints_processed.java","InvokeMethod_processed.java","ParameterizedAssertionErrorTest_processed.java","ClassRequest_processed.java","FilterFactories_processed.java","AllTests_processed.java","StackFilterTest_processed.java","RunWith_processed.java","UserStopTest_processed.java","TestWatchmanTest_processed.java","AllValidationTests_processed.java","Rule_processed.java","PotentialAssignmentTest_processed.java","Runner_processed.java","MethodSorter_processed.java","SortableTest_processed.java","AllTheoriesRunnerTests_processed.java","ValidationError_processed.java","ReverseAlphanumericSorter_processed.java","FailingDataPointMethods_processed.java","Enclosed_processed.java","FixMethodOrder_processed.java","AnnotationValidatorFactoryTest_processed.java","AnnotationsValidatorTest_processed.java","NoTestCaseClass_processed.java","TestWatcherTest_processed.java","NotVoidTestCase_processed.java","TestClassValidator_processed.java","SerializableMatcherDescription_processed.java","JUnit38ClassRunnerTest_processed.java","StringableObject_processed.java","AssertionFailedErrorTest_processed.java","LoggingTestRule_processed.java","package-info_processed.java","AnnotatedDescriptionTest_processed.java","DisableOnDebugTest_processed.java","RunnerTest_processed.java","GuesserQueue_processed.java","ArrayComparisonFailure_processed.java","Classes_processed.java","CategoryTest_processed.java","After_processed.java","OrderWithTest_processed.java","AnnotatedBuilder_processed.java","AllRunningTests_processed.java","WithNamedDataPoints_processed.java","JUnitSystem_processed.java","CommandLineTest_processed.java","CategoryFilterFactory_processed.java","ComparatorBasedOrdering_processed.java","Assignments_processed.java","BeforeClass_processed.java","CategoriesAndParameterizedTest_processed.java","TestTimedOutException_processed.java","TestDecorator_processed.java","TestRunListener_processed.java","InitializationErrorForwardCompatibilityTest_processed.java","AllDeprecatedTests_processed.java","BlockJUnit4ClassRunnerOverrideTest_processed.java","FrameworkMethod_processed.java","JUnitCore_processed.java","AllListeningTests_processed.java","TextFeedbackTest_processed.java","AllValidatorTests_processed.java","JUnitCoreReturnsCorrectExitCodeTest_processed.java","BlockJUnit4ClassRunnerWithParametersTest_processed.java","RunNotifier_processed.java","ThrowingRunnable_processed.java","RuntimeMXBean_processed.java","SpecificDataPointsSupplier_processed.java","TextRunnerSingleMethodTest_processed.java","AllRulesTests_processed.java","Alphanumeric_processed.java","StackTracesTest_processed.java","ClassRule_processed.java","AssumingInTheoriesTest_processed.java","TestFailure_processed.java","InvalidTestClassErrorTest_processed.java","BooleanSupplier_processed.java","ParametersRunnerFactory_processed.java","JUnit4ClassRunnerTest_processed.java","ResultMatchers_processed.java","ClassRequestTest_processed.java","Orderer_processed.java","TemporaryFolderRuleAssuredDeletionTest_processed.java","AllTheoriesTests_processed.java","MethodCall_processed.java","FrameworkMethodTest_processed.java","Request_processed.java","ExpectedExceptionTest_processed.java","TestRunner_processed.java","Verifier_processed.java","ReverseAlphanumericOrdering_processed.java","ClassRoadie_processed.java",) - var filename: String = "" - - lateinit var fileContents: String - - @Setup(Level.Trial) - fun prepare() { - fileContents = File(pathToInput + filename).readText() - } - - @Benchmark - @OutputTimeUnit(TimeUnit.NANOSECONDS) - fun measureAntlr(blackhole: Blackhole) { - val antlrParser = Java8Parser(CommonTokenStream(Java8Lexer(CharStreams.fromString(fileContents)))) - blackhole.consume(antlrParser.compilationUnit()) - } -} - -@State(Scope.Benchmark) -open class GllBenchmark { - @Param("OneTestCase_processed.java","NotPublicTestCase_processed.java","NoTestsRemainException_processed.java","OldTests_processed.java","TestRule_processed.java","JUnit4ClassRunner_processed.java","CategoryFilterFactoryTest_processed.java","TestClassTest_processed.java","ObjectContractTest_processed.java","LoggingStatement_processed.java","ThrowableMessageMatcher_processed.java","SortingRequest_processed.java","InheritedTestTest_processed.java","SerializableValueDescription_processed.java","JUnit3Builder_processed.java","FrameworkField_processed.java","JavadocTest_processed.java","SynchronizedRunListenerTest_processed.java","Ordering_processed.java","ClassRulesTest_processed.java","ComparisonCriteria_processed.java","TestDescriptionMethodNameTest_processed.java","JUnit4TestAdapterCache_processed.java","RuleContainer_processed.java","InvalidTestClassError_processed.java","JUnit4TestCaseFacade_processed.java","StubbedTheoriesTest_processed.java","FailedConstructionTest_processed.java","ReflectiveThreadMXBean_processed.java","Theory_processed.java","DataPoint_processed.java","Ignore_processed.java","ExpectedExceptionMatcherBuilder_processed.java","BlockJUnit4ClassRunnerWithParameters_processed.java","TestSystem_processed.java","TestWithParametersTest_processed.java","MultiCategoryTest_processed.java","AllMembersSupplier_processed.java","AnnotationsValidator_processed.java","ActiveTestSuite_processed.java","AssertTest_processed.java","RunListener_processed.java","Assume_processed.java","DataPoints_processed.java","TheoryTestUtils_processed.java","AllDefaultPossibilitiesBuilder_processed.java","TestRuleTest_processed.java","AllAssertionTests_processed.java","InvalidOrderingException_processed.java","ResultPrinter_processed.java","AllManipulationTests_processed.java","TextListenerTest_processed.java","Sortable_processed.java","ParameterizedNamesTest_processed.java","ParameterSignature_processed.java","RunnerBuilderStub_processed.java","ValidationTest_processed.java","StubbedTheories_processed.java","SuiteMethodBuilder_processed.java","AllRunnersTests_processed.java","PotentialAssignment_processed.java","StacktracePrintingMatcher_processed.java","Filterable_processed.java","SystemExitTest_processed.java","Filter_processed.java","MainRunner_processed.java","Result_processed.java","TemporaryFolderUsageTest_processed.java","AllTestsTest_processed.java","MultipleFailureException_processed.java","AssertionFailedError_processed.java","ParallelComputer_processed.java","AfterClass_processed.java","UseSuiteAsASuperclassTest_processed.java","ClassLevelMethodsWithIgnoredTestsTest_processed.java","MethodRulesTest_processed.java","Correspondent_processed.java","TypeMatchingBetweenMultiDataPointsMethod_processed.java","ActiveTestTest_processed.java","TestWatchman_processed.java","BadlyFormedClassesTest_processed.java","TestSuite_processed.java","MaxHistory_processed.java","AllParallelTests_processed.java","ComparisonCompactor_processed.java","ParameterSupplier_processed.java","AllClassesTests_processed.java","BlockJUnit4ClassRunnerWithParametersFactory_processed.java","AnnotatedBuilderTest_processed.java","AllExperimentalTests_processed.java","OverrideTestCase_processed.java","TempFolderRuleTest_processed.java","ComparisonFailureTest_processed.java","Parameterized_processed.java","ExpectExceptionTest_processed.java","PrintableResult_processed.java","ReflectiveRuntimeMXBean_processed.java","AllCoreTests_processed.java","ComparisonFailure_processed.java","RunAfters_processed.java","AlphanumericOrdering_processed.java","TestImplementorTest_processed.java","WithParameterSupplier_processed.java","WasRun_processed.java","MultipleFailureExceptionTest_processed.java","RuleChainTest_processed.java","TestListener_processed.java","Statement_processed.java","RepeatedTestTest_processed.java","BlockJUnit4ClassRunner_processed.java","FilterOptionIntegrationTest_processed.java","TestCaseTest_processed.java","ExpectedTest_processed.java","TextRunnerTest_processed.java","EnclosedTest_processed.java","InexactComparisonCriteria_processed.java","OrderWith_processed.java","IMoney_processed.java","UnsuccessfulWithDataPointFields_processed.java","Theories_processed.java","OrderableTest_processed.java","Protectable_processed.java","StacktracePrintingMatcherTest_processed.java","Description_processed.java","BlockJUnit4ClassRunnerTest_processed.java","ParentRunnerTest_processed.java","SuiteTest_processed.java","WithAutoGeneratedDataPoints_processed.java","ExpectException_processed.java","BaseTestRunnerTest_processed.java","TestDescriptionTest_processed.java","SynchronizedRunListener_processed.java","AllParameterizedTests_processed.java","AllModelTests_processed.java","Comparators_processed.java","ThreeTestCases_processed.java","RuleChain_processed.java","Computer_processed.java","TestClass_processed.java","SuiteDescriptionTest_processed.java","MaxCore_processed.java","CustomBlockJUnit4ClassRunnerTest_processed.java","MemoizingRequest_processed.java","ErrorReportingRunnerTest_processed.java","JUnit38ClassRunner_processed.java","TextListener_processed.java","FakeRuntimeMXBean_processed.java","PublicClassValidatorTest_processed.java","Timeout_processed.java","StopwatchTest_processed.java","ConcurrentRunNotifierTest_processed.java","TestCouldNotBeSkippedException_processed.java","Success_processed.java","LoggingMethodRule_processed.java","FilterFactoryParams_processed.java","AssumptionTest_processed.java","WithExtendedParameterSources_processed.java","FilterableTest_processed.java","AllDescriptionTests_processed.java","JUnit4_processed.java","AllRunnerTests_processed.java","SuiteMethodTest_processed.java","SingleMethodTest_processed.java","Describable_processed.java","JUnit4Builder_processed.java","FrameworkFieldTest_processed.java","IgnoreClassTest_processed.java","JUnitCommandLineParseResultTest_processed.java","MatcherTest_processed.java","ThrowableCauseMatcherTest_processed.java","AssumptionViolatedException_processed.java","CategoryValidatorTest_processed.java","ParentRunnerFilteringTest_processed.java","Orderable_processed.java","TestMethodTest_processed.java","ExternalResource_processed.java","ValidateWith_processed.java","AllCategoriesTests_processed.java","ExcludeCategories_processed.java","StoppedByUserException_processed.java","ParameterizedTestMethodTest_processed.java","FilterFactoriesTest_processed.java","RunBefores_processed.java","AllResultsTests_processed.java","ComparisonCompactorTest_processed.java","PrintableResultTest_processed.java","SampleJUnit3Tests_processed.java","ResultTest_processed.java","WithOnlyTestAnnotations_processed.java","Super_processed.java","TestedOn_processed.java","NullBuilder_processed.java","NoTestCases_processed.java","TestMethod_processed.java","Annotatable_processed.java","AssumptionViolatedExceptionTest_processed.java","NoArgTestCaseTest_processed.java","ErrorCollector_processed.java","AllSamplesTests_processed.java","RealSystem_processed.java","MethodSorterTest_processed.java","ForwardCompatibilityPrintingTest_processed.java","Guesser_processed.java","RepeatedTest_processed.java","TestSetup_processed.java","FilterFactory_processed.java","RuleContainerTest_processed.java","CouldNotReadCoreException_processed.java","ErrorReportingRunner_processed.java","FakeThreadMXBean_processed.java","VerifierRuleTest_processed.java","OrderWithValidator_processed.java","SpecificDataPointsSupplierTest_processed.java","WithDataPointMethod_processed.java","MethodValidator_processed.java","MemberValueConsumer_processed.java","ParentRunnerClassLoaderTest_processed.java","AllMethodsTests_processed.java","AllMembersSupplierTest_processed.java","RunRules_processed.java","ListenerTest_processed.java","RequestTest_processed.java","IgnoredBuilder_processed.java","IgnoredClassRunner_processed.java","SuiteMethod_processed.java","JUnitCoreTest_processed.java","TestResult_processed.java","NoGenericTypeParametersValidator_processed.java","RuleMemberValidator_processed.java","InitializationError_processed.java","Failure_processed.java","FilterTest_processed.java","ForwardCompatibilityTest_processed.java","RunnerBuilder_processed.java","AllTheoriesInternalTests_processed.java","IncludeCategories_processed.java","SuccessfulWithDataPointFields_processed.java","MaxStarterTest_processed.java","PublicClassValidator_processed.java","TypeSafeMatcher_processed.java","RuleMemberValidatorTest_processed.java","Stopwatch_processed.java","JUnitCommandLineParseResult_processed.java","RunWithTest_processed.java","TestWithParameters_processed.java","Categories_processed.java","MoneyBag_processed.java","JUnitMatchers_processed.java","FilterRequest_processed.java","ReguessableValue_processed.java","Stub_processed.java","OrderingRequest_processed.java","TimeoutRuleTest_processed.java","EnumSupplier_processed.java","JUnit38SortingTest_processed.java","AllInternalTests_processed.java","FailureList_processed.java","EventCollector_processed.java","MethodRule_processed.java","RunnerScheduler_processed.java","ErrorCollectorTest_processed.java","ArrayComparisonFailureTest_processed.java","InheritedTestCase_processed.java","ManagementFactory_processed.java","FailOnTimeout_processed.java","MethodSorters_processed.java","Category_processed.java","Checks_processed.java","AllJUnit3CompatibilityTests_processed.java","EachTestNotifier_processed.java","CategoryValidator_processed.java","ReflectiveCallable_processed.java","WithUnresolvedGenericTypeVariablesOnTheoryParms_processed.java","ParallelMethodTest_processed.java","TestWatcher_processed.java","TheoriesPerformanceTest_processed.java","TestListenerTest_processed.java","TestWithClassRule_processed.java","Fail_processed.java","ThrowableCauseMatcher_processed.java","DisableOnDebug_processed.java","AnnotationValidatorFactory_processed.java","RunnerSpy_processed.java","ParallelClassTest_processed.java","ParameterSignatureTest_processed.java","FrameworkMember_processed.java","TimeoutTest_processed.java","MethodRoadie_processed.java","OrderWithValidatorTest_processed.java","TemporaryFolder_processed.java","JUnit4TestAdapterTest_processed.java","NameRulesTest_processed.java","ListTest_processed.java","FailOnTimeoutTest_processed.java","ParameterizedAssertionError_processed.java","TestedOnSupplierTest_processed.java","TestName_processed.java","ParametersSuppliedBy_processed.java","Sub_processed.java","AllMaxTests_processed.java","Sorter_processed.java","ExternalResourceRuleTest_processed.java","ThreadsTest_processed.java","Version_processed.java","BaseTestRunner_processed.java","LoggingTestWatcher_processed.java","ExactComparisonCriteria_processed.java","Suite_processed.java","Before_processed.java","FailedBefore_processed.java","MoneyTest_processed.java","ExpectedException_processed.java","ExtensionTest_processed.java","RunNotifierTest_processed.java","OldTestClassAdaptingListenerTest_processed.java","Test_processed.java","AllNotificationTests_processed.java","ResultMatchersTest_processed.java","SampleJUnit4Tests_processed.java","ParentRunner_processed.java","AnnotationTest_processed.java","ThreadMXBean_processed.java","TestCase_processed.java","AnnotationValidator_processed.java","Money_processed.java","Assert_processed.java","DescriptionTest_processed.java","JUnit4TestAdapter_processed.java","WhenNoParametersMatch_processed.java","TestedOnSupplier_processed.java","ParameterizedTestTest_processed.java","FromDataPoints_processed.java","InvokeMethod_processed.java","ParameterizedAssertionErrorTest_processed.java","ClassRequest_processed.java","FilterFactories_processed.java","AllTests_processed.java","StackFilterTest_processed.java","RunWith_processed.java","UserStopTest_processed.java","TestWatchmanTest_processed.java","AllValidationTests_processed.java","Rule_processed.java","PotentialAssignmentTest_processed.java","Runner_processed.java","MethodSorter_processed.java","SortableTest_processed.java","AllTheoriesRunnerTests_processed.java","ValidationError_processed.java","ReverseAlphanumericSorter_processed.java","FailingDataPointMethods_processed.java","Enclosed_processed.java","FixMethodOrder_processed.java","AnnotationValidatorFactoryTest_processed.java","AnnotationsValidatorTest_processed.java","NoTestCaseClass_processed.java","TestWatcherTest_processed.java","NotVoidTestCase_processed.java","TestClassValidator_processed.java","SerializableMatcherDescription_processed.java","JUnit38ClassRunnerTest_processed.java","StringableObject_processed.java","AssertionFailedErrorTest_processed.java","LoggingTestRule_processed.java","package-info_processed.java","AnnotatedDescriptionTest_processed.java","DisableOnDebugTest_processed.java","RunnerTest_processed.java","GuesserQueue_processed.java","ArrayComparisonFailure_processed.java","Classes_processed.java","CategoryTest_processed.java","After_processed.java","OrderWithTest_processed.java","AnnotatedBuilder_processed.java","AllRunningTests_processed.java","WithNamedDataPoints_processed.java","JUnitSystem_processed.java","CommandLineTest_processed.java","CategoryFilterFactory_processed.java","ComparatorBasedOrdering_processed.java","Assignments_processed.java","BeforeClass_processed.java","CategoriesAndParameterizedTest_processed.java","TestTimedOutException_processed.java","TestDecorator_processed.java","TestRunListener_processed.java","InitializationErrorForwardCompatibilityTest_processed.java","AllDeprecatedTests_processed.java","BlockJUnit4ClassRunnerOverrideTest_processed.java","FrameworkMethod_processed.java","JUnitCore_processed.java","AllListeningTests_processed.java","TextFeedbackTest_processed.java","AllValidatorTests_processed.java","JUnitCoreReturnsCorrectExitCodeTest_processed.java","BlockJUnit4ClassRunnerWithParametersTest_processed.java","RunNotifier_processed.java","ThrowingRunnable_processed.java","RuntimeMXBean_processed.java","SpecificDataPointsSupplier_processed.java","TextRunnerSingleMethodTest_processed.java","AllRulesTests_processed.java","Alphanumeric_processed.java","StackTracesTest_processed.java","ClassRule_processed.java","AssumingInTheoriesTest_processed.java","TestFailure_processed.java","InvalidTestClassErrorTest_processed.java","BooleanSupplier_processed.java","ParametersRunnerFactory_processed.java","JUnit4ClassRunnerTest_processed.java","ResultMatchers_processed.java","ClassRequestTest_processed.java","Orderer_processed.java","TemporaryFolderRuleAssuredDeletionTest_processed.java","AllTheoriesTests_processed.java","MethodCall_processed.java","FrameworkMethodTest_processed.java","Request_processed.java","ExpectedExceptionTest_processed.java","TestRunner_processed.java","Verifier_processed.java","ReverseAlphanumericOrdering_processed.java","ClassRoadie_processed.java",) - var filename: String = "" - - val startState = JavaGrammar().rsm - - lateinit var fileContents: String - - @Setup(Level.Trial) - fun prepare() { - fileContents = File(pathToInput + filename).readText() - } - - @Benchmark - @OutputTimeUnit(TimeUnit.NANOSECONDS) - fun measureGll(blackhole: Blackhole) { - val inputGraph = getTokenStream(fileContents) - val gll = Gll.recoveryGll(startState, inputGraph) - - blackhole.consume(gll.parse()) - } -} diff --git a/benchmarks/src/jmh/kotlin/grammars/JavaGrammar.kt b/benchmarks/src/jmh/kotlin/grammars/JavaGrammar.kt deleted file mode 100644 index 36eeb9118..000000000 --- a/benchmarks/src/jmh/kotlin/grammars/JavaGrammar.kt +++ /dev/null @@ -1,546 +0,0 @@ -package grammars -import lexers.JavaToken -import org.ucfs.grammar.combinator.Grammar -import org.ucfs.grammar.combinator.regexp.* - -class JavaGrammar : Grammar() { - var CompilationUnit by Nt() - var Identifier by Nt() - var Literal by Nt() - var Type by Nt() - var PrimitiveType by Nt() - var ReferenceType by Nt() - var Annotation by Nt() - var NumericType by Nt() - var IntegralType by Nt() - var FloatingPointType by Nt() - var ClassOrInterfaceType by Nt() - var TypeVariable by Nt() - var ArrayType by Nt() - var ClassType by Nt() - var InterfaceType by Nt() - var TypeArguments by Nt() - var Dims by Nt() - var TypeParameter by Nt() - var TypeParameterModifier by Nt() - var TypeBound by Nt() - var AdditionalBound by Nt() - var TypeArgumentList by Nt() - var TypeArgument by Nt() - var Wildcard by Nt() - var WildcardBounds by Nt() - var TypeName by Nt() - var PackageOrTypeName by Nt() - var ExpressionName by Nt() - var AmbiguousName by Nt() - var MethodName by Nt() - var PackageName by Nt() - var Result by Nt() - var PackageDeclaration by Nt() - var ImportDeclaration by Nt() - var TypeDeclaration by Nt() - var PackageModifier by Nt() - var SingleTypeImportDeclaration by Nt() - var TypeImportOnDemandDeclaration by Nt() - var SingleStaticImportDeclaration by Nt() - var StaticImportOnDemandDeclaration by Nt() - var ClassDeclaration by Nt() - var InterfaceDeclaration by Nt() - var Throws by Nt() - var NormalClassDeclaration by Nt() - var EnumDeclaration by Nt() - var ClassModifier by Nt() - var TypeParameters by Nt() - var Superclass by Nt() - var Superinterfaces by Nt() - var ClassBody by Nt() - var TypeParameterList by Nt() - var InterfaceTypeList by Nt() - var ClassBodyDeclaration by Nt() - var ClassMemberDeclaration by Nt() - var InstanceInitializer by Nt() - var StaticInitializer by Nt() - var ConstructorDeclaration by Nt() - var FieldDeclaration by Nt() - var MethodDeclaration by Nt() - var FieldModifier by Nt() - var UnannType by Nt() - var VariableDeclaratorList by Nt() - var VariableDeclarator by Nt() - var VariableDeclaratorId by Nt() - var VariableInitializer by Nt() - var Expression by Nt() - var ArrayInitializer by Nt() - var UnannPrimitiveType by Nt() - var UnannReferenceType by Nt() - var UnannClassOrInterfaceType by Nt() - var UnannTypeVariable by Nt() - var UnannArrayType by Nt() - var UnannClassType by Nt() - var UnannInterfaceType by Nt() - var MethodModifier by Nt() - var MethodHeader by Nt() - var MethodBody by Nt() - var MethodDeclarator by Nt() - var FormalParameterList by Nt() - var ReceiverParameter by Nt() - var FormalParameters by Nt() - var LastFormalParameter by Nt() - var FormalParameter by Nt() - var VariableModifier by Nt() - var ExceptionTypeList by Nt() - var ExceptionType by Nt() - var Block by Nt() - var ConstructorModifier by Nt() - var ConstructorDeclarator by Nt() - var ConstructorBody by Nt() - var SimpleTypeName by Nt() - var ExplicitConstructorInvocation by Nt() - var EnumBody by Nt() - var EnumConstantList by Nt() - var EnumConstant by Nt() - var EnumConstantModifier by Nt() - var EnumBodyDeclarations by Nt() - var BlockStatements by Nt() - var ArgumentList by Nt() - var Primary by Nt() - var NormalInterfaceDeclaration by Nt() - var InterfaceModifier by Nt() - var ExtendsInterfaces by Nt() - var InterfaceBody by Nt() - var InterfaceMemberDeclaration by Nt() - var ConstantDeclaration by Nt() - var ConstantModifier by Nt() - var AnnotationTypeDeclaration by Nt() - var AnnotationTypeBody by Nt() - var AnnotationTypeMemberDeclaration by Nt() - var AnnotationTypeElementDeclaration by Nt() - var DefaultValue by Nt() - var NormalAnnotation by Nt() - var ElementValuePairList by Nt() - var ElementValuePair by Nt() - var ElementValue by Nt() - var ElementValueArrayInitializer by Nt() - var ElementValueList by Nt() - var MarkerAnnotation by Nt() - var SingleElementAnnotation by Nt() - var InterfaceMethodDeclaration by Nt() - var AnnotationTypeElementModifier by Nt() - var ConditionalExpression by Nt() - var VariableInitializerList by Nt() - var BlockStatement by Nt() - var LocalVariableDeclarationStatement by Nt() - var LocalVariableDeclaration by Nt() - var Statement by Nt() - var StatementNoShortIf by Nt() - var StatementWithoutTrailingSubstatement by Nt() - var EmptyStatement by Nt() - var LabeledStatement by Nt() - var LabeledStatementNoShortIf by Nt() - var ExpressionStatement by Nt() - var StatementExpression by Nt() - var IfThenStatement by Nt() - var IfThenElseStatement by Nt() - var IfThenElseStatementNoShortIf by Nt() - var AssertStatement by Nt() - var SwitchStatement by Nt() - var SwitchBlock by Nt() - var SwitchBlockStatementGroup by Nt() - var SwitchLabels by Nt() - var SwitchLabel by Nt() - var EnumConstantName by Nt() - var WhileStatement by Nt() - var WhileStatementNoShortIf by Nt() - var DoStatement by Nt() - var InterfaceMethodModifier by Nt() - var ForStatement by Nt() - var ForStatementNoShortIf by Nt() - var BasicForStatement by Nt() - var BasicForStatementNoShortIf by Nt() - var ForInit by Nt() - var ForUpdate by Nt() - var StatementExpressionList by Nt() - var EnhancedForStatement by Nt() - var EnhancedForStatementNoShortIf by Nt() - var BreakStatement by Nt() - var ContinueStatement by Nt() - var ReturnStatement by Nt() - var ThrowStatement by Nt() - var SynchronizedStatement by Nt() - var TryStatement by Nt() - var Catches by Nt() - var CatchClause by Nt() - var CatchFormalParameter by Nt() - var CatchType by Nt() - var Finally by Nt() - var TryWithResourcesStatement by Nt() - var ResourceSpecification by Nt() - var ResourceList by Nt() - var Resource by Nt() - var PrimaryNoNewArray by Nt() - var ClassLiteral by Nt() - var classOrInterfaceTypeToInstantiate by Nt() - var UnqualifiedClassInstanceCreationExpression by Nt() - var ClassInstanceCreationExpression by Nt() - var FieldAccess by Nt() - var TypeArgumentsOrDiamond by Nt() - var ArrayAccess by Nt() - var MethodInvocation by Nt() - var MethodReference by Nt() - var ArrayCreationExpression by Nt() - var DimExprs by Nt() - var DimExpr by Nt() - var LambdaExpression by Nt() - var LambdaParameters by Nt() - var InferredFormalParameterList by Nt() - var LambdaBody by Nt() - var AssignmentExpression by Nt() - var Assignment by Nt() - var LeftHandSide by Nt() - var AssignmentOperator by Nt() - var ConditionalOrExpression by Nt() - var ConditionalAndExpression by Nt() - var InclusiveOrExpression by Nt() - var ExclusiveOrExpression by Nt() - var AndExpression by Nt() - var EqualityExpression by Nt() - var RelationalExpression by Nt() - var ShiftExpression by Nt() - var AdditiveExpression by Nt() - var MultiplicativeExpression by Nt() - var PreIncrementExpression by Nt() - var PreDecrementExpression by Nt() - var UnaryExpressionNotPlusMinus by Nt() - var UnaryExpression by Nt() - var PostfixExpression by Nt() - var PostIncrementExpression by Nt() - var PostDecrementExpression by Nt() - var CastExpression by Nt() - var ConstantExpression by Nt() - - init { - Identifier = JavaToken.ID - - Literal = JavaToken.INTEGERLIT or JavaToken.FLOATINGLIT or JavaToken.BOOLEANLIT or - JavaToken.CHARLIT or JavaToken.STRINGLIT or JavaToken.NULLLIT - - /** - * Productions from §4 (Types, Values, and Variables) - */ - Type = PrimitiveType or ReferenceType - PrimitiveType = Many(Annotation) * NumericType or Many(Annotation) * JavaToken.BOOLEAN - NumericType = IntegralType or FloatingPointType - IntegralType = JavaToken.BYTE or JavaToken.SHORT or JavaToken.INT or JavaToken.LONG or JavaToken.CHAR - FloatingPointType = JavaToken.FLOAT or JavaToken.DOUBLE - ReferenceType = ClassOrInterfaceType or TypeVariable or ArrayType - ClassOrInterfaceType = ClassType or InterfaceType - ClassType = Many(Annotation) * Identifier * Option(TypeArguments) or - ClassOrInterfaceType * JavaToken.DOT * Many(Annotation) * Identifier * Option(TypeArguments) - InterfaceType = ClassType - TypeVariable = Many(Annotation) * Identifier - ArrayType = PrimitiveType * Dims or ClassOrInterfaceType * Dims or TypeVariable * Dims - Dims = Some(Many(Annotation) * JavaToken.BRACKETLEFT * JavaToken.BRACKETRIGHT) - TypeParameter = Many(TypeParameterModifier) * Identifier * Option(TypeBound) - TypeParameterModifier = Annotation - TypeBound = JavaToken.EXTENDS * TypeVariable or JavaToken.EXTENDS * ClassOrInterfaceType * Many(AdditionalBound) - AdditionalBound = JavaToken.ANDBIT * InterfaceType - TypeArguments = JavaToken.LT * TypeArgumentList * JavaToken.GT - TypeArgumentList = TypeArgument * Many(JavaToken.COMMA * TypeArgument) - TypeArgument = ReferenceType or Wildcard - Wildcard = Many(Annotation) * JavaToken.QUESTIONMARK * Option(WildcardBounds) - WildcardBounds = JavaToken.EXTENDS * ReferenceType or JavaToken.SUPER * ReferenceType - - /** - * Productions from §6 (Names) - */ - - TypeName = Identifier or PackageOrTypeName * JavaToken.DOT * Identifier - PackageOrTypeName = Identifier or PackageOrTypeName * JavaToken.DOT * Identifier - ExpressionName = Identifier or AmbiguousName * JavaToken.DOT * Identifier - MethodName = Identifier - PackageName = Identifier or PackageName * JavaToken.DOT * Identifier - AmbiguousName = Identifier or AmbiguousName * JavaToken.DOT * Identifier - - /** - * Productions from §7 (Packages) - */ - - CompilationUnit = Option(PackageDeclaration) * Many(ImportDeclaration) * Many(TypeDeclaration) - PackageDeclaration = Many(PackageModifier) * JavaToken.PACKAGE * Identifier * Many(JavaToken.DOT * Identifier) * JavaToken.SEMICOLON - PackageModifier = Annotation - ImportDeclaration = SingleTypeImportDeclaration or TypeImportOnDemandDeclaration or - SingleStaticImportDeclaration or StaticImportOnDemandDeclaration - SingleTypeImportDeclaration = JavaToken.IMPORT * TypeName * JavaToken.SEMICOLON - TypeImportOnDemandDeclaration = JavaToken.IMPORT * PackageOrTypeName * JavaToken.DOT * JavaToken.STAR * JavaToken.SEMICOLON - SingleStaticImportDeclaration = JavaToken.IMPORT * JavaToken.STATIC * TypeName * JavaToken.DOT * Identifier * JavaToken.SEMICOLON - StaticImportOnDemandDeclaration = JavaToken.IMPORT * JavaToken.STATIC * TypeName * JavaToken.DOT * JavaToken.STAR * JavaToken.SEMICOLON - TypeDeclaration = ClassDeclaration or InterfaceDeclaration or JavaToken.SEMICOLON - - /** - * Productions from §8 (Classes) - */ - - ClassDeclaration = NormalClassDeclaration or EnumDeclaration - NormalClassDeclaration = Many(ClassModifier) * JavaToken.CLASS * Identifier * - Option(TypeParameters) * Option(Superclass) * Option(Superinterfaces) * ClassBody - ClassModifier = Annotation or JavaToken.PUBLIC or JavaToken.PROTECTED or JavaToken.PRIVATE or - JavaToken.ABSTRACT or JavaToken.STATIC or JavaToken.FINAL or JavaToken.STRICTFP - TypeParameters = JavaToken.LT * TypeParameterList * JavaToken.GT - TypeParameterList = TypeParameter * Many(JavaToken.COMMA * TypeParameter) - Superclass = JavaToken.EXTENDS * ClassType - Superinterfaces = JavaToken.IMPLEMENTS * InterfaceTypeList - InterfaceTypeList = InterfaceType * Many(JavaToken.COMMA * InterfaceType) - ClassBody = JavaToken.CURLYLEFT * Many(ClassBodyDeclaration) * JavaToken.CURLYRIGHT - ClassBodyDeclaration = ClassMemberDeclaration or InstanceInitializer or StaticInitializer or ConstructorDeclaration - ClassMemberDeclaration = FieldDeclaration or MethodDeclaration or ClassDeclaration or InterfaceDeclaration or JavaToken.SEMICOLON - FieldDeclaration = Many(FieldModifier) * UnannType * VariableDeclaratorList * JavaToken.SEMICOLON - FieldModifier = Annotation or JavaToken.PUBLIC or JavaToken.PROTECTED or JavaToken.PRIVATE or JavaToken.STATIC or - JavaToken.FINAL or JavaToken.TRANSIENT or JavaToken.VOLATILE - VariableDeclaratorList = VariableDeclarator * Many(JavaToken.COMMA * VariableDeclarator) - VariableDeclarator = VariableDeclaratorId * Option(JavaToken.ASSIGN * VariableInitializer) - VariableDeclaratorId = Identifier * Option(Dims) - VariableInitializer = Expression or ArrayInitializer - UnannType = UnannPrimitiveType or UnannReferenceType - UnannPrimitiveType = NumericType or JavaToken.BOOLEAN - UnannReferenceType = UnannClassOrInterfaceType or UnannTypeVariable or UnannArrayType - UnannClassOrInterfaceType = UnannClassType or UnannInterfaceType - UnannClassType = Identifier * Option(TypeArguments) or - UnannClassOrInterfaceType * JavaToken.DOT * Many(Annotation) * Identifier * Option(TypeArguments) - UnannInterfaceType = UnannClassType - UnannTypeVariable = Identifier - UnannArrayType = UnannPrimitiveType * Dims or UnannClassOrInterfaceType * Dims or UnannTypeVariable * Dims - MethodDeclaration = Many(MethodModifier) * MethodHeader * MethodBody - MethodModifier = Annotation or JavaToken.PUBLIC or JavaToken.PROTECTED or JavaToken.PRIVATE or JavaToken.ABSTRACT or - JavaToken.STATIC or JavaToken.FINAL or JavaToken.SYNCHRONIZED or JavaToken.NATIVE or JavaToken.STRICTFP - MethodHeader = Result * MethodDeclarator * Option(Throws) or - TypeParameters * Many(Annotation) * Result * MethodDeclarator * Option(Throws) - Result = UnannType or JavaToken.VOID - MethodDeclarator = Identifier * JavaToken.PARENTHLEFT * Option(FormalParameterList) * JavaToken.PARENTHRIGHT * Option(Dims) - FormalParameterList = ReceiverParameter or FormalParameters * JavaToken.COMMA * LastFormalParameter or - LastFormalParameter - FormalParameters = FormalParameter * Many(JavaToken.COMMA * FormalParameter) or - ReceiverParameter * Many(JavaToken.COMMA * FormalParameter) - FormalParameter = Many(VariableModifier) * UnannType * VariableDeclaratorId - VariableModifier = Annotation or JavaToken.FINAL - LastFormalParameter = Many(VariableModifier) * UnannType * Many(Annotation) * JavaToken.ELLIPSIS * VariableDeclaratorId or FormalParameter - ReceiverParameter = Many(Annotation) * UnannType * Option(Identifier * JavaToken.DOT) * JavaToken.THIS - Throws = JavaToken.THROWS * ExceptionTypeList - ExceptionTypeList = ExceptionType * Many(JavaToken.COMMA * ExceptionType) - ExceptionType = ClassType or TypeVariable - MethodBody = Block or JavaToken.SEMICOLON - InstanceInitializer = Block - StaticInitializer = JavaToken.STATIC * Block - ConstructorDeclaration = Many(ConstructorModifier) * ConstructorDeclarator * Option(Throws) * ConstructorBody - ConstructorModifier = Annotation or JavaToken.PUBLIC or JavaToken.PROTECTED or JavaToken.PRIVATE - ConstructorDeclarator = Option(TypeParameters) * SimpleTypeName * JavaToken.PARENTHLEFT * Option(FormalParameterList) * JavaToken.PARENTHRIGHT - SimpleTypeName = Identifier - ConstructorBody = JavaToken.CURLYLEFT * Option(ExplicitConstructorInvocation) * Option(BlockStatements) * JavaToken.CURLYRIGHT - ExplicitConstructorInvocation = Option(TypeArguments) * JavaToken.THIS * JavaToken.PARENTHLEFT * Option(ArgumentList) * JavaToken.PARENTHRIGHT * JavaToken.SEMICOLON or - Option(TypeArguments) * JavaToken.SUPER * JavaToken.PARENTHLEFT * Option(ArgumentList) * JavaToken.PARENTHRIGHT * JavaToken.SEMICOLON or - ExpressionName * JavaToken.DOT * Option(TypeArguments) * JavaToken.SUPER * JavaToken.PARENTHLEFT * Option(ArgumentList) * JavaToken.PARENTHRIGHT * JavaToken.SEMICOLON or - Primary * JavaToken.DOT * Option(TypeArguments) * JavaToken.SUPER * JavaToken.PARENTHLEFT * Option(ArgumentList) * JavaToken.PARENTHRIGHT * JavaToken.SEMICOLON - EnumDeclaration = Many(ClassModifier) * JavaToken.ENUM * Identifier * Option(Superinterfaces) * EnumBody - EnumBody = JavaToken.CURLYLEFT * Option(EnumConstantList) * Option(JavaToken.COMMA) * Option(EnumBodyDeclarations) * JavaToken.CURLYRIGHT - EnumConstantList = EnumConstant * Many(JavaToken.COMMA * EnumConstant) - EnumConstant = Many(EnumConstantModifier) * Identifier * Option(JavaToken.PARENTHLEFT * Option(ArgumentList) * JavaToken.PARENTHRIGHT * Option(ClassBody)) - EnumConstantModifier = Annotation - EnumBodyDeclarations = JavaToken.SEMICOLON * Many(ClassBodyDeclaration) - - /** - * Productions from §9 (Interfaces) - */ - - InterfaceDeclaration = NormalInterfaceDeclaration or AnnotationTypeDeclaration - NormalInterfaceDeclaration = - Many(InterfaceModifier) * JavaToken.INTERFACE * Identifier * Option(TypeParameters) * Option(ExtendsInterfaces) * InterfaceBody - InterfaceModifier = Annotation or JavaToken.PUBLIC or JavaToken.PROTECTED or JavaToken.PRIVATE or - JavaToken.ABSTRACT or JavaToken.STATIC or JavaToken.STRICTFP - ExtendsInterfaces = JavaToken.EXTENDS * InterfaceTypeList - InterfaceBody = JavaToken.CURLYLEFT * Many(InterfaceMemberDeclaration) * JavaToken.CURLYRIGHT - InterfaceMemberDeclaration = ConstantDeclaration or InterfaceMethodDeclaration or ClassDeclaration or InterfaceDeclaration or JavaToken.SEMICOLON - ConstantDeclaration = Many(ConstantModifier) * UnannType * VariableDeclaratorList * JavaToken.SEMICOLON - ConstantModifier = Annotation or JavaToken.PUBLIC or JavaToken.STATIC or JavaToken.FINAL - InterfaceMethodDeclaration = Many(InterfaceMethodModifier) * MethodHeader * MethodBody - InterfaceMethodModifier = Annotation or JavaToken.PUBLIC or JavaToken.ABSTRACT or JavaToken.DEFAULT or JavaToken.STATIC or JavaToken.STRICTFP - AnnotationTypeDeclaration = Many(InterfaceModifier) * JavaToken.AT * JavaToken.INTERFACE * Identifier * AnnotationTypeBody - AnnotationTypeBody = JavaToken.CURLYLEFT * Many(AnnotationTypeMemberDeclaration) * JavaToken.CURLYRIGHT - AnnotationTypeMemberDeclaration = AnnotationTypeElementDeclaration or ConstantDeclaration or ClassDeclaration or InterfaceDeclaration or JavaToken.SEMICOLON - AnnotationTypeElementDeclaration = - Many(AnnotationTypeElementModifier) * UnannType * Identifier * JavaToken.PARENTHLEFT * JavaToken.PARENTHRIGHT * Option(Dims) * Option(DefaultValue) * JavaToken.SEMICOLON - AnnotationTypeElementModifier = Annotation or JavaToken.PUBLIC or JavaToken.ABSTRACT - DefaultValue = JavaToken.DEFAULT * ElementValue - Annotation = NormalAnnotation or MarkerAnnotation or SingleElementAnnotation - NormalAnnotation = JavaToken.AT * TypeName * JavaToken.PARENTHLEFT * Option(ElementValuePairList) * JavaToken.PARENTHRIGHT - ElementValuePairList = ElementValuePair * Many(JavaToken.COMMA * ElementValuePair) - ElementValuePair = Identifier * JavaToken.ASSIGN * ElementValue - ElementValue = ConditionalExpression or ElementValueArrayInitializer or Annotation - ElementValueArrayInitializer = JavaToken.CURLYLEFT * Option(ElementValueList) * Option(JavaToken.COMMA) * JavaToken.CURLYRIGHT - ElementValueList = ElementValue * Many(JavaToken.COMMA * ElementValue) - MarkerAnnotation = JavaToken.AT * TypeName - SingleElementAnnotation = JavaToken.AT * TypeName * JavaToken.PARENTHLEFT * ElementValue * JavaToken.PARENTHRIGHT - - /** - * Productions from §10 (Arrays) - */ - - ArrayInitializer = JavaToken.CURLYLEFT * Option(VariableInitializerList) * Option(JavaToken.COMMA) * JavaToken.CURLYRIGHT - VariableInitializerList = VariableInitializer * Many(JavaToken.COMMA * VariableInitializer) - - /** - * Productions from §14 (Blocks and Statements) - */ - - Block = JavaToken.CURLYLEFT * Option(BlockStatements) * JavaToken.CURLYRIGHT - BlockStatements = BlockStatement * Many(BlockStatement) - BlockStatement = LocalVariableDeclarationStatement or ClassDeclaration or Statement - LocalVariableDeclarationStatement = LocalVariableDeclaration * JavaToken.SEMICOLON - LocalVariableDeclaration = Many(VariableModifier) * UnannType * VariableDeclaratorList - Statement = StatementWithoutTrailingSubstatement or LabeledStatement or IfThenStatement or IfThenElseStatement or - WhileStatement or ForStatement - StatementNoShortIf = StatementWithoutTrailingSubstatement or LabeledStatementNoShortIf or IfThenElseStatementNoShortIf or - WhileStatementNoShortIf or ForStatementNoShortIf - StatementWithoutTrailingSubstatement = Block or EmptyStatement or ExpressionStatement or AssertStatement or - SwitchStatement or DoStatement or BreakStatement or ContinueStatement or ReturnStatement or SynchronizedStatement or - ThrowStatement or TryStatement - EmptyStatement = JavaToken.SEMICOLON - LabeledStatement = Identifier * JavaToken.COLON * Statement - LabeledStatementNoShortIf = Identifier * JavaToken.COLON * StatementNoShortIf - ExpressionStatement = StatementExpression * JavaToken.SEMICOLON - StatementExpression = Assignment or PreIncrementExpression or PreDecrementExpression or PostIncrementExpression or - PostDecrementExpression or MethodInvocation or ClassInstanceCreationExpression - IfThenStatement = JavaToken.IF * JavaToken.PARENTHLEFT * Expression * JavaToken.PARENTHRIGHT * Statement - IfThenElseStatement = JavaToken.IF * JavaToken.PARENTHLEFT * Expression * JavaToken.PARENTHRIGHT * StatementNoShortIf * JavaToken.ELSE * Statement - IfThenElseStatementNoShortIf = - JavaToken.IF * JavaToken.PARENTHLEFT * Expression * JavaToken.PARENTHRIGHT * StatementNoShortIf * JavaToken.ELSE * StatementNoShortIf - AssertStatement = JavaToken.ASSERT * Expression * JavaToken.SEMICOLON or - JavaToken.ASSERT * Expression * JavaToken.COLON * Expression * JavaToken.SEMICOLON - SwitchStatement = JavaToken.SWITCH * JavaToken.PARENTHLEFT * Expression * JavaToken.PARENTHRIGHT * SwitchBlock - SwitchBlock = JavaToken.CURLYLEFT * Many(SwitchBlockStatementGroup) * Many(SwitchLabel) * JavaToken.CURLYRIGHT - SwitchBlockStatementGroup = SwitchLabels * BlockStatements - SwitchLabels = Some(SwitchLabel) - SwitchLabel = JavaToken.CASE * ConstantExpression * JavaToken.COLON or - JavaToken.CASE * EnumConstantName * JavaToken.COLON or JavaToken.DEFAULT * JavaToken.COLON - EnumConstantName = Identifier - WhileStatement = JavaToken.WHILE * JavaToken.PARENTHLEFT * Expression * JavaToken.PARENTHRIGHT * Statement - WhileStatementNoShortIf = JavaToken.WHILE * JavaToken.PARENTHLEFT * Expression * JavaToken.PARENTHRIGHT * StatementNoShortIf - DoStatement = JavaToken.DO * Statement * JavaToken.WHILE * JavaToken.PARENTHLEFT * Expression * JavaToken.PARENTHRIGHT * JavaToken.SEMICOLON - ForStatement = BasicForStatement or EnhancedForStatement - ForStatementNoShortIf = BasicForStatementNoShortIf or EnhancedForStatementNoShortIf - BasicForStatement = JavaToken.FOR * JavaToken.PARENTHLEFT * Option(ForInit) * JavaToken.SEMICOLON * Option(Expression) * JavaToken.SEMICOLON * Option(ForUpdate) * JavaToken.PARENTHRIGHT * Statement - BasicForStatementNoShortIf = JavaToken.FOR * JavaToken.PARENTHLEFT * Option(ForInit) * JavaToken.SEMICOLON * Option(Expression) * JavaToken.SEMICOLON * Option(ForUpdate) * JavaToken.PARENTHRIGHT * StatementNoShortIf - ForInit = StatementExpressionList or LocalVariableDeclaration - ForUpdate = StatementExpressionList - StatementExpressionList = StatementExpression * Many(JavaToken.COMMA * StatementExpression) - EnhancedForStatement = JavaToken.FOR * JavaToken.PARENTHLEFT * Many(VariableModifier) * UnannType * VariableDeclaratorId * JavaToken.COLON * Expression * JavaToken.PARENTHRIGHT * Statement - EnhancedForStatementNoShortIf = JavaToken.FOR * JavaToken.PARENTHLEFT * Many(VariableModifier) * UnannType * VariableDeclaratorId * JavaToken.COLON * Expression * JavaToken.PARENTHRIGHT * StatementNoShortIf - BreakStatement = JavaToken.BREAK * Option(Identifier) * JavaToken.SEMICOLON - ContinueStatement = JavaToken.CONTINUE * Option(Identifier) * JavaToken.SEMICOLON - ReturnStatement = JavaToken.RETURN * Option(Expression) * JavaToken.SEMICOLON - ThrowStatement = JavaToken.THROW * Expression * JavaToken.SEMICOLON - SynchronizedStatement = JavaToken.SYNCHRONIZED * JavaToken.PARENTHLEFT * Expression * JavaToken.PARENTHRIGHT * Block - TryStatement = JavaToken.TRY * Block * Catches or JavaToken.TRY * Block * Option(Catches) * Finally or TryWithResourcesStatement - Catches = Some(CatchClause) - CatchClause = JavaToken.CATCH * JavaToken.PARENTHLEFT * CatchFormalParameter * JavaToken.PARENTHRIGHT * Block - CatchFormalParameter = Many(VariableModifier) * CatchType * VariableDeclaratorId - CatchType = UnannClassType * Many(JavaToken.ORBIT * ClassType) - Finally = JavaToken.FINALLY * Block - TryWithResourcesStatement = JavaToken.TRY * ResourceSpecification * Block * Option(Catches) * Option(Finally) - ResourceSpecification = JavaToken.PARENTHLEFT * ResourceList * Option(JavaToken.SEMICOLON) * JavaToken.PARENTHRIGHT - ResourceList = Resource * Many(JavaToken.COMMA * Resource) - Resource = Many(VariableModifier) * UnannType * VariableDeclaratorId * JavaToken.ASSIGN * Expression - - /** - * Productions from §15 (Expressions) - */ - - Primary = PrimaryNoNewArray or ArrayCreationExpression - PrimaryNoNewArray = Literal or ClassLiteral or JavaToken.THIS or TypeName * JavaToken.DOT * JavaToken.THIS or - JavaToken.PARENTHLEFT * Expression * JavaToken.PARENTHRIGHT or ClassInstanceCreationExpression or FieldAccess or - ArrayAccess or MethodInvocation or MethodReference - ClassLiteral = TypeName * Many(JavaToken.BRACKETLEFT * JavaToken.BRACKETRIGHT) * JavaToken.DOT * JavaToken.CLASS or - NumericType * Many(JavaToken.BRACKETLEFT * JavaToken.BRACKETRIGHT) * JavaToken.DOT * JavaToken.CLASS or - JavaToken.BOOLEAN * Many(JavaToken.BRACKETLEFT * JavaToken.BRACKETRIGHT) * JavaToken.DOT * JavaToken.CLASS or - JavaToken.VOID * JavaToken.DOT * JavaToken.CLASS - ClassInstanceCreationExpression = UnqualifiedClassInstanceCreationExpression or - ExpressionName * JavaToken.DOT * UnqualifiedClassInstanceCreationExpression or - Primary * JavaToken.DOT * UnqualifiedClassInstanceCreationExpression - UnqualifiedClassInstanceCreationExpression = - JavaToken.NEW * Option(TypeArguments) * classOrInterfaceTypeToInstantiate * JavaToken.PARENTHLEFT * Option(ArgumentList) * JavaToken.PARENTHRIGHT * Option(ClassBody) - classOrInterfaceTypeToInstantiate = Many(Annotation) * Identifier * Many(JavaToken.DOT * Many(Annotation) * Identifier) * Option(TypeArgumentsOrDiamond) - TypeArgumentsOrDiamond = TypeArguments or JavaToken.LT * JavaToken.GT - FieldAccess = Primary * JavaToken.DOT * Identifier or JavaToken.SUPER * JavaToken.DOT * Identifier or - TypeName * JavaToken.DOT * JavaToken.SUPER * JavaToken.DOT * Identifier - ArrayAccess = ExpressionName * JavaToken.BRACKETLEFT * Expression * JavaToken.BRACKETRIGHT or - PrimaryNoNewArray * JavaToken.BRACKETLEFT * Expression * JavaToken.BRACKETRIGHT - MethodInvocation = MethodName * JavaToken.PARENTHLEFT * Option(ArgumentList) * JavaToken.PARENTHRIGHT or - TypeName * JavaToken.DOT * Option(TypeArguments) * Identifier * JavaToken.PARENTHLEFT * Option(ArgumentList) * JavaToken.PARENTHRIGHT or - ExpressionName * JavaToken.DOT * Option(TypeArguments) * Identifier * JavaToken.PARENTHLEFT * Option(ArgumentList) * JavaToken.PARENTHRIGHT or - Primary * JavaToken.DOT * Option(TypeArguments) * Identifier * JavaToken.PARENTHLEFT * Option(ArgumentList) * JavaToken.PARENTHRIGHT or - JavaToken.SUPER * JavaToken.DOT * Option(TypeArguments) * Identifier * JavaToken.PARENTHLEFT * Option(ArgumentList) * JavaToken.PARENTHRIGHT or - TypeName * JavaToken.DOT * JavaToken.SUPER * JavaToken.DOT * Option(TypeArguments) * Identifier * JavaToken.PARENTHLEFT * Option(ArgumentList) * JavaToken.PARENTHRIGHT - ArgumentList = Expression * Many(JavaToken.COMMA * Expression) - MethodReference = ExpressionName * JavaToken.DOUBLECOLON * Option(TypeArguments) * Identifier or - ReferenceType * JavaToken.DOUBLECOLON * Option(TypeArguments) * Identifier or - Primary * JavaToken.DOUBLECOLON * Option(TypeArguments) * Identifier or - JavaToken.SUPER * JavaToken.DOUBLECOLON * Option(TypeArguments) * Identifier or - TypeName * JavaToken.DOT * JavaToken.SUPER * JavaToken.DOUBLECOLON * Option(TypeArguments) * Identifier or - ClassType * JavaToken.DOUBLECOLON * Option(TypeArguments) * JavaToken.NEW or - ArrayType * JavaToken.DOUBLECOLON * JavaToken.NEW - ArrayCreationExpression = JavaToken.NEW * PrimitiveType * DimExprs * Option(Dims) or - JavaToken.NEW * ClassOrInterfaceType * DimExprs * Option(Dims) or - JavaToken.NEW * PrimitiveType * Dims * ArrayInitializer or - JavaToken.NEW * ClassOrInterfaceType * Dims * ArrayInitializer - DimExprs = Some(DimExpr) - DimExpr = Many(Annotation) * JavaToken.BRACKETLEFT * Expression * JavaToken.BRACKETRIGHT - Expression = LambdaExpression or AssignmentExpression - LambdaExpression = LambdaParameters * JavaToken.ARROW * LambdaBody - LambdaParameters = Identifier or JavaToken.PARENTHLEFT * Option(FormalParameterList) * JavaToken.PARENTHRIGHT or - JavaToken.PARENTHLEFT * InferredFormalParameterList * JavaToken.PARENTHRIGHT - InferredFormalParameterList = Identifier * Many(JavaToken.COMMA * Identifier) - LambdaBody = Expression or Block - AssignmentExpression = ConditionalExpression or Assignment - Assignment = LeftHandSide * AssignmentOperator * Expression - LeftHandSide = ExpressionName or FieldAccess or ArrayAccess - AssignmentOperator = JavaToken.ASSIGN or JavaToken.STARASSIGN or JavaToken.SLASHASSIGN or JavaToken.PERCENTASSIGN or JavaToken.PLUSASSIGN or JavaToken.MINUSASSIGN or - JavaToken.SHIFTLEFTASSIGN or JavaToken.SHIFTRIGHTASSIGN or JavaToken.USRIGHTSHIFTASSIGN or JavaToken.ANDASSIGN or JavaToken.XORASSIGN or JavaToken.ORASSIGN - ConditionalExpression = ConditionalOrExpression or - ConditionalOrExpression * JavaToken.QUESTIONMARK * Expression * JavaToken.COLON * ConditionalExpression or - ConditionalOrExpression * JavaToken.QUESTIONMARK * Expression * JavaToken.COLON * LambdaExpression - ConditionalOrExpression = ConditionalAndExpression or - ConditionalOrExpression * JavaToken.OR * ConditionalAndExpression - ConditionalAndExpression = InclusiveOrExpression or - ConditionalAndExpression * JavaToken.AND * InclusiveOrExpression - InclusiveOrExpression = ExclusiveOrExpression or - InclusiveOrExpression * JavaToken.ORBIT * ExclusiveOrExpression - ExclusiveOrExpression = AndExpression or ExclusiveOrExpression * JavaToken.XORBIT * AndExpression - AndExpression = EqualityExpression or AndExpression * JavaToken.ANDBIT * EqualityExpression - EqualityExpression = RelationalExpression or EqualityExpression * JavaToken.EQ * RelationalExpression or - EqualityExpression * JavaToken.NOTEQ * RelationalExpression - RelationalExpression = ShiftExpression or RelationalExpression * JavaToken.LT * ShiftExpression or - RelationalExpression * JavaToken.GT * ShiftExpression or RelationalExpression * JavaToken.LESSEQ * ShiftExpression or - RelationalExpression * JavaToken.GREATEQ * ShiftExpression or RelationalExpression * JavaToken.INSTANCEOF * ReferenceType - ShiftExpression = AdditiveExpression or ShiftExpression * JavaToken.LT * JavaToken.LT * AdditiveExpression or - ShiftExpression * JavaToken.GT * JavaToken.GT * AdditiveExpression or - ShiftExpression * JavaToken.GT * JavaToken.GT * JavaToken.GT * AdditiveExpression - AdditiveExpression = MultiplicativeExpression or AdditiveExpression * JavaToken.PLUS * MultiplicativeExpression or - AdditiveExpression * JavaToken.MINUS * MultiplicativeExpression - MultiplicativeExpression = UnaryExpression or MultiplicativeExpression * JavaToken.STAR * UnaryExpression or - MultiplicativeExpression * JavaToken.SLASH * UnaryExpression or - MultiplicativeExpression * JavaToken.PERCENT * UnaryExpression - UnaryExpression = PreIncrementExpression or PreDecrementExpression or JavaToken.PLUS * UnaryExpression or - JavaToken.MINUS * UnaryExpression or UnaryExpressionNotPlusMinus - PreIncrementExpression = JavaToken.PLUSPLUS * UnaryExpression - PreDecrementExpression = JavaToken.MINUSMINUS * UnaryExpression - UnaryExpressionNotPlusMinus = PostfixExpression or JavaToken.TILDA * UnaryExpression or JavaToken.EXCLAMATIONMARK * UnaryExpression or - CastExpression - PostfixExpression = Primary or ExpressionName or PostIncrementExpression or PostDecrementExpression - PostIncrementExpression = PostfixExpression * JavaToken.PLUSPLUS - PostDecrementExpression = PostfixExpression * JavaToken.MINUSMINUS - CastExpression = JavaToken.PARENTHLEFT * PrimitiveType * JavaToken.PARENTHRIGHT * UnaryExpression or - JavaToken.PARENTHLEFT * ReferenceType * Many(AdditionalBound) * JavaToken.PARENTHRIGHT * UnaryExpressionNotPlusMinus or - JavaToken.PARENTHLEFT * ReferenceType * Many(AdditionalBound) * JavaToken.PARENTHRIGHT * LambdaExpression - ConstantExpression = Expression - - setStart(CompilationUnit) - } -} \ No newline at end of file diff --git a/benchmarks/src/jmh/kotlin/jmhGenerated.kt b/benchmarks/src/jmh/kotlin/jmhGenerated.kt new file mode 100644 index 000000000..3515d1afa --- /dev/null +++ b/benchmarks/src/jmh/kotlin/jmhGenerated.kt @@ -0,0 +1,109 @@ +package jmh.kotlin + +import getTokenStream +import org.antlr.Java8Lexer +import org.antlr.Java8Parser +import org.antlr.v4.runtime.CharStreams +import org.antlr.v4.runtime.CommonTokenStream +import org.srcgll.Gll +import org.srcgll.ReachabilityMode +import org.srcgll.RecoveryMode +import org.srcgll.lexer.JavaGrammar +import org.openjdk.jmh.annotations.* +import org.openjdk.jmh.infra.Blackhole +import org.srcgll.sppf.buildStringFromSppf +import java.io.File +import java.util.concurrent.TimeUnit +import java.util.logging.FileHandler +import java.util.logging.Logger +import java.util.logging.SimpleFormatter + +val logger: Logger = Logger.getLogger("Logger") +val fh: FileHandler = FileHandler("Benchmarks.log", true) +val pathToInput = "./../../src/jmh/src_files_processed/" + +@State(Scope.Benchmark) +@BenchmarkMode(Mode.SingleShotTime) +open class AntlrBenchmark { + + @Param("14032585_516614449","23994681_898810829","37101673_1399232436","6205548_217411567","2567500_135221580","26375825_1002865961","32900055_1235347381","18260055_667305453","44180763_1682824831","13770384_504745021","2702736_94884261","49123149_1877552666","34043419_1277924737","42386067_1617181307","46044461_1756727857","15235194_577364081","10401182_367359496","8383839_293638316","31888163_1194402894","40879792_1555206827","20462736_745665883","8832616_308018249","27151556_1015634282","4234547_145977831","3678398_128763544","30566883_1145190267","39758500_1508082355","2212886_75934168","13697485_501983091","18636474_681527769","42345481_1615557639","48423156_1850708889","2203298_77116149","46104673_1759178220","24051981_887335257","5914594_206527376","23538442_865441635","2798933_101467001","41437037_1577888889","37740815_1420072604","42145027_1608087466","7277363_257926123","26813604_1123555794","10303550_363408086","1276265_37767456","39089518_1478979392","26452060_1007479131","21125576_767193023","10539839_373417275","37354606_1401755257","19812868_724341129","1254315_36926861","4495524_154927023","27542734_1029867468","12467970_585500935","14745131_541032872","11102340_408168235","43746724_1666094162","35143319_1315142795","36854485_1384748997","46671895_1781546607","12634139_461076692","13606347_498597341","2114489_72166941","48092030_1875818509","34555035_1301895570","2253643_77570060","36537754_1376267456","40581595_1542530326","22377301_815252532","48631853_1859167895","49286204_1883692570","22807757_834195621","6421698_225644501","37952147_1427926535","7778055_274280044","3039524_106797694","31033000_1162406475","8773075_306085641","28100733_1061975890","39538162_1498683675","14317410_523758466","35822277_1339143567","26760179_1008825511","7379020_263549919","27199602_1017452207","38083666_1433877665","41035980_1561711021","37494133_1410230970","1670322_54178120","46525281_1775381431","3889612_134895079","27942864_1044342483","12770800_466451028","1692415_55080761","21982667_798338656","43498530_1656755285","28442884_1063094250","2222194_146344952","17643132_643728845","35830397_1339407387","5519479_191988146","17706871_646514850","7129442_252733531","39597357_1503072885","36304420_1357504166","5856744_204290134","12268323_446473327","29994206_1123001795","17174449_626153509","40734349_1549062329","16732569_610006438","5929428_207739384","45153516_1721244569","7219906_256116890","5590187_194628000","37746353_1419242560","22080277_802468954","14060914_514944448","37522402_1408940219","2040979_69237261","9479231_329610943","46185881_1765225025","43188837_1645327377","22996332_842363630","9056200_315058909","1940747_65160008","49038047_1874199796","11412004_410835576","45494222_1734910712","17136987_633448536","18359430_671072384","51062_1046501","43207049_1645982636","10206664_359325128","16327805_596571061","35862733_1342712619","23952779_951257173","1044491_29997907","37001209_1386188141","16609481_605504644","8293143_290749925","34612088_1303889179","46541430_1776527223","34934233_1308412560","5519153_191950698","24163448_921835113","22490783_820243142","37455682_1406170689","5663561_197087373","23228465_852257328","24977760_928375777","4584701_158096701","4554980_158174410","38190145_1438725541","42335103_1615259581","30494756_1148984974","871822_22044242","3629597_127330529","16276686_593522731","8956272_311874142","11584606_418073237","40547139_1541222371","15444366_562964888","8073474_284382416","12949451_475940773","1312896_39258805","2138508_73117603","30024829_1123999680","50190_1021069","19660211_719458755","11967859_439033608","12992293_480521962","16354271_596234812","23475959_913702154","33202112_1258773470","31916781_1195546198","30390418_1138120290","41159575_1566638844","5001055_208346382","11815307_427733906","1190299_34444356","29108187_1089031084","31585010_1182593594","21714655_922462715","40416210_1535898741","21825937_792001857","4473330_154106147","22767022_835411287","22681544_828885626","38922623_1471208367","1684825_56051090","48814347_1865817364","46466007_1779312168","34528527_1298814866","37082486_1389748213","27674410_1034532860","40888509_1561865756","42191270_1609800777","1070020_30682542","13304347_491821013","10962352_391767259","1721047_56371820","13821505_506533208","10943274_390988108","48755877_1863930410","35937919_1343345438","10091193_354525053","39121708_1489709623","41352252_1574214693","12454740_453864748","42921668_1635768371","3412031_137845152","12759538_468408265","40577240_1542420553","36636993_1370237641","41671513_1587895355","6777804_239460436","9498372_330390829","35657368_1658586609","21559783_781912388","37772035_1420382289","40389052_1534864882","28900452_1080965690","5181478_181904393","1662579_53913974","27919361_1043519438","44507651_1695364358","5002847_173150916","35647298_1332932098","11866530_429780260","40799541_1551826425","1878318_62625937","5771639_201045828","14828846_541243509","43772232_1679236679","5004158_177495161","43585036_1659796846","22391407_821021811","42252285_1750316359","40352475_1533125020","22787815_833407862","28885504_1144430075","39300475_1488037474","44637849_1707818141","44759203_1708069234","35642082_1332782672","21975267_798031604","228034_5348091","20534436_748165418","40956007_1559611293","46741139_1783903693","34525040_1294552347","23639523_870078703","20245045_742251831","15037924_548590114","3428288_120386125","39511066_1497290582","13644580_500082026","16504101_601855616","37617247_1419478023","20794910_756773732","6317091_221757433","39781326_1509095169","33302359_1260250935","25498279_949865055","10580767_375225260","9936530_348381631","7709878_272111119","9008149_313967678","2150859_73502769","39851655_1514757309","14900785_543464824","38596827_1456759408","40501615_1539263511","6390201_229901502","42246912_1611790175","26328364_983456346","40903335_1556198944","7745239_273132718","40282189_1535829169","37151525_1392746729","30276123_1133746683","23815247_892198660","1423982_67244612","22788624_833396891","37010668_1386568707","26942404_1007851114","40542978_1545853101","13369753_489548268","20960225_763526337","15994523_583005856","36776020_1376332628","7696770_271745949","36161892_1351771917","3220872_113430599","38704578_1461692788","25485788_955297387","24936374_942129999","11796249_441216204","36177913_1354133223","33591682_1261278997","19101433_698707472","15682832_575503033","44970163_1713821464","19216068_702709875","23854516_878661536","4793526_165687734","6206150_217466803","41389684_1576072299","31981308_1198249607","18806089_687770677","26160736_976928084","22406969_816557338","9985671_350396941","35336032_1322170355","36273940_1356177068","28539494_1066874590","1849136_61491564","49139325_1878123017","20536190_748237186","1884823_62861257","47702961_1821766880","17639771_643442115","11772040_425790935","39686525_1561704631","41879695_1596798701","28306024_1059025094","10390480_366972163","48345284_1849897422","39016801_1475531023","16034031_605418494","11615198_419284506","1741244_57080128","10246882_360999031","31625403_1184231957","40418556_1537808273","5897255_206915280","7837971_277314575","25089513_932533092","4784049_165406296","11083787_398828344","45357515_1729580639","9009074_314031072","18429375_673818324","44277849_1686420948","2146683_77644181","18479646_675539569","35332674_1322010559","1563916_49646095","5821791_203045947","18138322_662748851","1565882_49721418","41337732_1573740212","7125435_252846287","17222436_627909779","47229678_1803247128","47462737_1812280898","10122585_359935574","47828083_1827239668","17405290_634480320","48532746_1855272923","30212143_1131265600","30815377_1154142583","28344272_1059530945","267431_6177313","11649740_420652584","28217171_1054618597","23043408_844404355","23096072_881601576","28986950_1084368157","24601991_911540142","11407132_410625235","44905101_1718580880","8676791_313646765","9338392_324550666","14311931_523699900","22207138_807889618","22836533_835354152","11959559_433546629","40096085_1522415627","12175720_444979101","34969954_1309478506","38286863_1442678153","23473208_862567879","21022230_764084497","38622851_1457892593","4707453_175915663","49124396_1877589520","16953658_617985664","2831822_99630754","34812941_1304317829","49312308_1885621092","3519005_123630450","36154282_1355468183","47895606_1840135694","1545449_48897711","18873910_693741673","22036001_800619994","604529_13831332","2148272_73431878","38332785_1444876067","44796843_1731563673","3658363_128219477","23568492_872118367","28478983_1064951023","21474988_778866739","36900272_1384277855","14403870_526325009","19513269_713216254","22084411_802660651","48179344_1841305216","5295489_183615403","28471625_1064204080","4951685_171400707","28731670_1074474201","46619556_1779127064","40699340_1587812418","5380671_187433425","36742101_1380381221","28182334_1053269228","44470099_1719401832","39994037_1518262180","28049769_1048316939","40484897_1539228655","16251988_592646875","25082089_936928450","19796563_723699507","45073783_1717836308","35425738_1325190812","1197161_34698346","1079718_30051892","42592068_1624526221","32248408_1209260737","11698625_424855398","1721305_57413864","45672459_1742260779","40076439_1521627729","40584759_1543208877","22252824_809966777","12839621_469356263","20467366_759140781","44493313_1694668449","35071654_1314479085","22516905_821472079","29933664_1120547319","31036911_1162254206","10139534_356645253","11082820_396859915","33462985_1256437027","37086733_1389868364","28471320_1064250200","31458501_1178677941","959458_25455156","17564126_640460186","18383409_676258199","9600860_334273152","18275438_667885322","38525359_1453754271","11023819_394471458","11857249_429419795","10926053_390312658","9802109_342769250","42815652_1632221102","11646202_420541690","12381706_450933492","38241808_1443147292","34678744_1302180083","6571552_231364555","41872657_1596442876","15740242_593779962","35713826_1335438134","9484453_329824118","24475158_1278505796","28162556_1060507118","22688950_829274767","39252113_1486064147","2085440_71015699","8960064_314559106","49257277_1882412897","23338140_856992586","15033101_551445216","1078315_31597280","20013091_730860966","36094382_1349767240","41748692_1594475403","2810143_98799915","23961237_883426905","28411214_1061957041","15799576_577700223","45926614_1787636853","49083376_1876039825","38212667_1439865933","20003610_730573924","1969564_66243819","4909874_170245167","4238176_146089696","43238559_1647070934","9470313_338179118","20895721_760175493","24737950_937037702","37262328_1397548853","7167969_254086309","22607848_825587799","44487384_1694447611","26504786_991044310","14639691_534434533","13470184_505533833","13431444_491974436","49048959_1875042069","38901991_1470282424","38042025_1432118299","29671062_1110658112","26945699_1007979714","41254504_1570438656","37612146_1413046017","18391029_672456341","9771196_363019395","3147026_110706816","43784475_1667462391","11335444_407460135","6718606_237001259","37658800_1415146245","35500108_1327776688","40337124_1532607963","35190816_1316798625","14109345_516584701","35678784_1334099188","49326115_1884979220","21830930_792222381","17674399_658621718","27491977_1029050979","32033329_1328601378","43958406_1673837980","26982737_1009382720","23134891_848104671","37669101_1415547213","7876128_277488838","19849538_725590233","22882984_838139888","16778354_611940589","23622223_868973326","16127641_590104230","36642814_1370620254","41621489_1592732693","36828663_1378651395","2994253_105305024","33818392_1271178288","40695231_1581710921","29406723_1100463768","13652652_500396289","44260282_1698798267","12111291_440059435","12044022_437097842","12853719_470045039","15817525_576716923","7884539_280301655","4233909_145973453","11837715_444461252","28180136_1053176664","6651213_234429860","18914671_692909051","18148702_675343535","2123320_72432446","25861111_964930180","22141579_805044423","11196222_401854551","20267072_750914697","39168548_1482452714","13874115_508574273","30192908_1130647956","9757726_340888717","12496329_458221385","45631673_1740462861","20264139_739000824","1771938_58305193","29858364_1122085724","41472428_1582664169","4060674_146643474","22390540_815825605","37258395_1397462644","36175887_1352290618","22675029_828610673","21444548_777757920","23096072_862253744","33855646_1276980613","16843908_614133600","16056816_585442918","34000073_1276276594","19793285_723598488","33153081_1244981667","15149802_552519370","27495175_1028282788","35164665_1315889041","5062799_181825045","43561008_1658967181","23204703_851122356","40912575_1561730204","28386486_1061084922","36238268_1365227176","37009392_1386492004","37459079_1406281153","6266493_225337789","42067529_1604925874","1747807_59976697","3462841_121615234","19472758_711848494","39559404_1514537955","47362259_1808243186","49056569_1887684476","10529588_375160623","43409921_1653433458","37185550_1394151764","38514863_1532994723","432925_10357884","37606463_1412806455","36679428_1372545608","24178034_893218308","43376655_1679778212","35627414_1332236807","11343409_438294472","1539240_48626998","13933571_510665785","41749225_1608227466","26259433_983730029","48827514_1866347163","42843041_1633144368","47817621_1855102229","48568174_1856676911","17192340_626757845","2530485_88510785","44366352_1695697544","24362217_900774196","47590757_1817507553","33135117_1249104362","38282073_1442421466","38356042_1452902640","13053204_477728211","1452751_48102085","4853130_167893359","22350781_814305773","31315742_1172470780","24969792_931334426","3653238_128283864","44324640_1688180738","32039670_1201443678","13351027_488864947","14067332_515105539","22629584_828767659","11003213_420480441","48921217_1872210144","42580899_1629894147","22935244_839951722","29446948_1102113082","24992139_928462548","22749479_831714974","35066861_1312600808","35251532_1319025925","2268020_78199411","44751617_1704903501","26075993_974557900","41979723_1603784813","23194857_853577119","6189627_216817295","23737694_886714371","37066746_1388919478","47980212_1833138636","47037395_1798203228","42345849_1740729334","15570753_570998257","39788950_1540549269","25727127_959493528","29653764_1110002905","36287149_1356752263","27361408_1023556009","22535963_822410948","27645915_1033479847","23350877_857544928","1639278_52950687","4960435_174362415","24628631_915538259","17260739_629143176","30684431_1149131410","33882331_1280612970","10226778_360085637","35137134_1315003110","26645892_1004185367","4596122_158560736","849376_21209231","36006689_1347416843","29979921_1122374216","25787422_962091064","33472129_1256786335","8382910_336222845","13319594_487518761","31704347_1187436082","18462396_696730626","26957371_1008462019","19796228_723692334","5757937_200528044","13679251_507876139","36032672_1348245953","40679512_1546631339","9694345_338137844","24213007_894572088","48278304_1845423133","29131015_1089780045","24081707_888712087","1049113_28850165","48903092_1869193330","19926396_728201228","17808349_649860875","39521834_1497813673","29468654_1102791235","3971743_137509130","3387946_120306608","8368826_293197245","6873879_243125979","10383324_366677203","11572527_419968425","48483356_1853078429","6660346_234735495","4954550_171915839","32159727_1205690492","5903899_256049779","31939682_1196549894","49532186_1898868952","30215108_1131352520","15716344_572999679","29457134_1102604097","6264123_220017786","45365257_1729862784","23918385_881303570","42977669_1637658058","42321840_1614717930","17894203_652996529","49557424_1893537523","24101641_889642729","4498235_155018402","6390283_224565723","45666991_1742062671","38721157_1462172749","21808954_791417320","47369229_1808577719","33693151_1265001167","39739682_1511278712","8753583_305481633","36235090_1354548201","11272890_407444882","9448222_328450542","1320214_39504487","2788951_98144129","37686470_1416309291","24559538_909587432","23037773_844313528","48479800_1866437440","40246668_1528605784","13322261_487605288","49326558_1884989824","12928036_472964614","33227529_1247908199","298854_7029160","15852699_578669618","4745628_164082160","10598344_376069675","35907340_1349754212","2323123_80392100","4334791_149508606","7906592_280602663","40197020_1526500027","47558461_1816297617","49481353_1893766240","10528259_372886896","30121216_1127800590","33962879_1274961539","36147180_1355051144","34874831_1306430544","10582078_375283000","41566778_1583336977","44429011_1834438688","41148307_1566639884","3111653_111077159","45597473_1739126639","2890818_101617866","18727220_684918716","26994502_1009907728","2130638_72703580","43341057_1654389985","26156971_993603492","11933643_432588045","9593476_334134555","29499906_1115610778","35158744_1315707884","40057548_1520752327","23524850_864896080","14101365_516330180","41312243_1573018338","43312947_1652315916","13457948_492876494","41650551_1590633223","44263969_1691597626","48896652_1869027254","26686874_997632782","32376027_1214418269","45619075_1787225805","6233771_218741767","35187464_1317029177","41259382_1570688911","2523648_88164699","32391638_1215146297","10131136_369377030","22790126_838885469","18501135_676439914","21430110_777216280","48809094_1865794966","44859064_1709265472","43731184_1665500053","29483400_1105301765","10270379_361893425","30727797_1150719892","26365732_984913818","38366963_1452897381","34070170_1279452623","835052_20693935","18118994_662098345","36654996_1371116332","7703741_271908154","5530084_192353359","30744059_1151315962","18071997_660073101","33862663_1272156889","26082605_974000911","34717995_1301234731","1499064_46952642","33031513_1240443731","34985356_1310060132","27814362_1039538435","25096661_937779063","10475909_379313320","24215964_894660108","37175570_1397211040","10669352_389042402","46032435_1756370951","29784936_1114886832","1141996_37645162","23488180_863190831","16761896_625241393","30516571_1158708906","8366926_294402297","20307302_740334316","17344968_635953472","39135805_1481652617","44584781_1727663650","13838016_507196882","7127354_252579169","30399910_1145222315","7877637_277526806","10582122_377134782","30986492_1172080741","8552197_299049971","3957237_137951692","28122567_1095682856","35932400_1343158558","41929597_1598800982","26533613_991782023","23466781_862342541","11603961_418817369","18485331_680701652","15591782_568425062","29539858_1112262764","2988933_105119706","35546679_1329399427","15172778_600068993","25489430_949436245","1838974_63442638","5055369_174862164","11159527_400152451","9573552_333214658","48980685_1872151835","14636080_534321455","7819732_306289986","39573435_1499970795","11554977_416917692","49136603_1877981266","37636276_1414198456","15931849_580814593","39188916_1486802039","36724858_1374062297","39934679_1515824284","19848934_730701504","4152106_143342917","5458202_189666509","36934516_1383343203","11300006_406039829","14504630_530093364","4601642_158803965","5255394_182015760","14011604_513276700","48800531_1865282411","9649350_336230707","49353159_1885931520","3322265_152860600","43048704_1641732360","10192454_358812012","46654840_1780616256","26902022_1006306552","13981866_513841453","1795605_70406850","23449744_861476269","10763582_385601326","28242365_1055491597","41986757_1601198815","24213436_895764628","41225614_1569468114","17080768_625265320","47611925_1818541662","14734179_539245878","12863185_470300763","46617728_1781279798","23518228_864484920","39565139_1499662186","10396922_367205418","24668146_914351061","30831002_1174276506","47071332_1797069990","33708851_1265601784","10274663_362169921","44472232_1693924024","5860568_204472849","20285357_739629158","36545597_1366721763","31285444_1171484025","36452495_1364906780","28557463_1067575687","19304990_713025632","4953175_171456802","48497200_1853687319","26454180_990638075","12966869_474366789","46754576_1784505791","26806099_1002427423","32658995_1226058166","1172365_33791936","32884209_1234795165","35318834_1321537139","21433629_779748681","37396397_1403685243","16686145_613289811","1415535_50350394","45535446_1736795883","12710119_464108433","31851655_1193128623","46468264_1773253694","23509692_866473364","36567331_1367497624","26800361_1002189140","29057255_1086996002","22347761_822183475","36387065_1361955665","25354035_944245595","32096740_1204148656","36857207_1379880448","41177957_1567517257","44254145_1685631139","44349184_1712004568","5899672_205933835","13183853_482565766","478615_11111585","26774265_1037917254","13371438_489597244","17056600_622284838","26718049_998863301","37960964_1428377894","39196294_1483635379","11636239_420102910","13353275_488919878","37199309_1400534240","28125098_1051081906","46894102_1794637895","13465833_497376069","35810263_1339697843","32896176_1239447059","17530256_639101176","2148116_73403951","6447978_226497861","17432188_635750202","43319522_1658004245","48903980_1869505376","22604980_825497112","15981271_582570916","12388754_477087071","36982007_1385372980","25417139_946423544","23172745_849733703","1924610_66175897","12102647_439580766","578230_13404470","11637905_420141534","17590204_641526303","9059546_315154286","30833926_1159882607","2215078_76029115","6201184_241716643","15341809_559431666","37205435_1398004181","10155285_357225781","41262965_1583937703","19985852_730190098","28377783_1060733122","44274485_1686298202","42418660_1618263760","5970254_208568790","36475915_1363868099","36718594_1415591202","20678098_752773533","21429038_777228991","39109712_1490334982","24772461_918842430","22919021_839187289","46022763_1758737640","18725041_720960042","45881361_1750415636","8312710_291407377","6865140_242802927","38130594_1436202406","5477469_190354642","5578016_194077510","19636905_723334460","10150168_370937386","4152289_149016529","40743875_1551694955","2156075_76848758","16736729_638397335","29320076_1097310300","38240324_1442385537","26009224_970973477","2709840_95244518","23953282_882895548","649500_15832385","10903176_389268052","37480444_1407319562","28019691_1056446968","33324801_1251274859","27592464_1031552471","22598896_829256683","21983190_798380554","10619429_377087409","17831778_650679117","38665203_1463650717","3204643_112917834","26277664_994902766","31629456_1184402104","22891928_837967292","33558510_1260095378","44063330_1678014486","18818726_688296931","41176397_1567301442","38202963_1439193777","39281667_1487238009","30210847_1131282618","48613237_1858453188","45952605_1753018986","6934872_245404874","31315721_1172484980","15010436_547476441","23181426_850317772","45500284_1735184861","25791237_962231070","22768937_835140987","6116979_214067176","38507187_1452734231","33587641_1289861820","8695253_303660946","34494113_1297168329","33494436_1257634189","35670953_1333809400","5084978_175925684","46336623_1768174970","18146939_663034648","12186430_443008589","34336820_1288151064","37339421_1401067274","47944684_1852403157","16851266_616253475","25791350_969454993","21967727_797667705","23664824_870869499","22234818_838323391","25517596_1044227986","13118027_484876578","36736980_1374710891","11817408_427805193","41359557_1574501012","5272812_182744369","25345116_963549644","18266230_667525878","47784772_1826295130","18679155_683080652","45830733_1748600236","30183483_1130167230","10934103_390612214","11335179_412570422","40417958_1604507055","35286712_1323261151","36547782_1373079960","33014000_1239765195","48125411_1867356628","30746834_1159558838","26034487_972023247","30490714_1146936287","3295478_118674908","29469428_1103599657","39949604_1516399501","27238461_1019003903","38256164_1444008716","9772750_346099219","20867790_759274846","38324969_1444412063","22971897_841422749","29140415_1090193272","21373520_775332264","22723809_830547953","46913420_1790830537","25809755_963041010","1458189_45304424","31421882_1176964041","48577773_1856978474","13452670_503028326","10898105_388995275","25770319_969008505","47247307_1844764015","1287836_38243801","11153176_420338693","22798488_833846130","31333120_1179884622","39281149_1487273861","17892932_653029302","6638372_233898997","48823201_1866148878","13850241_507721571","35032611_1311452832","49076314_1875782221","4527987_156127222","36840693_1379140611","41658762_1587364524","43712062_1664582709","32162212_1210707520","47650463_1819970341","33120821_1243835453","38608068_1458894269","11439956_425353562","1447866_46645913","33069531_1241774797","17602955_642846284","3094943_108715623","47470303_1812968440","39471668_1495469230","4866654_168371872","20258239_740092953","917761_33653544","27149660_1015556926","18772981_689870557","24790150_919664308","23663587_870820668","1847614_61421955","36963135_1444944930","36101821_1350177554","19585108_715984180","13919646_510212617","10628963_377506220","24966607_927482774","18693817_683594653","20653851_754532016","35163604_1315846751","22777317_832955301","40969485_1558930078","20524212_747813583","12242698_445453843","36245340_1354999657","19369413_708265097","1862761_62085791","198004_4848477","14891628_546341353","17547844_639762566","38294959_1446201165","856051_21443571","28646403_1071259127","21669307_786089080","12968796_474494113","32886082_1238328066","31609834_1183551228","44209058_1688294008","46001866_1755271945","25796984_962593468","37483697_1407598581","1818745_60272910","16656929_607211069","39036050_1476470337","20743981_755090407","41782205_1611290125","9524404_333628946","28651942_1078477929","43584653_1664913001","10007658_351327563","40613216_1543834943","39240768_1637166162","36225153_1354611563","6564575_238813989","9851064_344652980","2019490_68543429","16377824_597127826","27042664_1019730814","35276007_1319819580","25053898_931091765","36234277_1354568647","37982534_1429472337","5131095_177666735","37108592_1390873078","43781504_1667298596","984011_26280371","18511882_677246989","44600058_1700525468","3314468_116729708","27941949_1051240798","39404753_1492519363","23194036_850670220","22646655_827362845","542209_12530517","32524331_1220511924","20030096_731377195","5090148_176135052","5343040_185868895","46005568_1755313932","31452839_1177719067","48300895_1846197169","28052873_1048704034","20712447_758942939","39198603_1487136272","48244814_1850916413","35781590_1337628951","3158902_111195627","39272920_1486848464","48452628_1868792720","26141786_976229891","40043748_1530342293","17537123_639349657","26630757_1003710225","2638325_92357477","21929219_796124388","24568842_910270308","8603388_300701822","9338215_324546126","15257863_558579991","6993370_251209488","31342299_1173624114","32216537_1207972301","40791308_1551584872","6012527_210119728","17672304_644614261","34043389_1277894765","3016860_116233607","27761095_1039136825","15383363_564284879","11359975_408476410","48989025_1872537798","11196424_401841236","25364161_944413919","18724632_695171388","23378428_868147320","10376502_367964459","12664975_462455020","8349454_292620407","12460872_460659915","37293259_1398907220","33064649_1241600449","28564393_1083258186","42026957_1605305132","21348622_774511259","9918088_347565822","11922954_433018555","47836272_1827016467","1182436_34136171","46825712_1787365045","44003460_1675588591","22231079_808926594","18091773_660835165","39682832_1506489328","2909950_102374371","11527870_416081663","35810639_1338686547","16541502_608773124","16389239_597532823","47270870_1804857024","1720243_56361113","40358952_1536714073","18636978_681548851","29666823_1110452097","47001926_1794217904","9591338_333900946","16273934_601945501","45773952_1749200231","40407623_1535522990","22232707_809015066","21614532_783940271","1316307_39404386","22776438_832812989","17843430_654279144","42172760_1609037561","12733712_467198255","26527752_991666230","30847922_1155325063","48600967_1858624310","31328633_1176227345","10987513_393077681","13614084_498933194","36516052_1365410163","22434377_817773228","38856979_1468520556","17689664_645263534","6704361_236465011","31975746_1212304147","39651071_1503459046","45197830_1722814238","38750327_1519954154","40568534_1542030942","12032049_436551199","22668382_828359849","40703958_1550011635","21691324_786969611","32816805_1241900042","42875805_1635219581","11172021_400656255","37176415_1393771073","36809313_1377997047","49217457_1882122052","8213122_288098466","25980510_969740177","37357110_1413258331","40669233_1546306709","44796394_1706699681","34665977_1299413130","37504870_1408333734","6018322_210424392","46797534_1786272031","40486424_1538639292","23865217_879540168","21920515_797844314","6355488_235899102","2195959_78094130","48245514_1844144858","7027222_249039736","18332211_670089796","31235318_1169613079","750202_19234046","30032070_1151887984","40403924_1549288711","41506168_1581007720","12635099_494971652","914513_29318632","35802442_1338388768","9783587_341986497","26887706_1005786893","48904676_1869268471","19136814_705283993","43705014_1664247587","49606320_1895307325","29891616_1126329706","47733798_1822938816","5089559_249457764","36121860_1350307928","41302188_1572331392","38400783_1451206545","12821050_468567780","24401397_902627186","23116392_847963747","24335441_899801099","4272563_154711814","17522255_714324224","31041574_1162479241","21351288_774595397","4585859_158115219","34236151_1284658676","28430053_1062706161","2214647_75967932","42036061_1603228846","36064171_1348040027","44114162_1680058840","10996783_397923347","10972921_392223033","5975315_208828902","21081590_765765729","30381450_1137827659","2467034_161582253","10990160_392942163","24434205_903914037","18179624_664311623","26712548_1008659270","43015759_1639063723","17280498_639969634","18658000_685362475","11327907_410807748","443244_10358161","7663942_270619402","43883912_1671833434","38468481_1451037340","16837455_620719325","6067577_216647386","17546494_639721112","49624411_1895899854","38389044_1447582579","13755481_504113182","42607605_1625063626","35215956_1317767456","32151385_1205341864","39822272_1510778499","18347042_670562855","42827177_1632568269","35280540_1328739183","48897311_1869109683","32938809_1239855989","18595127_682866902","12897073_471764266","24660318_914025028","26088836_976628556","11560252_417167174","14336038_535872876","36512404_1367437551","25681145_957426188","10491566_371348960","3027638_111896754","25720849_959195382","17639314_646430047","15453734_563452042","23367480_874037438","7570548_267867240","35146512_1315279791","24444839_904403849","24205954_894410919","5959192_212134268","21509869_780068551","10934610_390597620","32419048_1247787153","10533397_373070746","32431712_1216720355","14937040_544717876","13364741_489327515","38694781_1460919862","3904896_135386486","24976797_934059281","37157949_1393011448","39548403_1498868750","18523257_677212116","953003_42595981","32844984_1243472952","36215604_1353834214","13066570_478223836","11976074_434290596","33827663_1282123100","2747711_96557750","1040452_126196409","47318039_1807150239","16033910_584468599","43122864_1644942358","28873994_1079902712","26668255_1012089415","39133665_1480783576","23520963_864628884","27914158_1043204707","38026454_1434776425","32509253_1219827766","24571308_910313703","2463645_85904962","30166309_1143687668","359897_8795933","39567089_1502293558","16912523_654477621","31141168_1166009539","1406033_43761192","21417864_818139355","2192583_75221027","30988742_1229006980","39373998_1491067247","40282941_1530121410","2358226_81903007","6471959_227548939","6045280_211434817","12045756_437245210","4947760_171282610","9312269_323632108","40693599_1547219440","48141064_1845571405","40586820_1549530460","41980939_1609790938","15490684_564683501","2015891_68152446","10433544_368814928","4863383_168279448","43387695_1652615810","1588668_50709819","26697928_998000825","41824509_1594349839","26892962_1005992100","8785905_306520979","5385527_187065370","3454671_121370158","46211963_1763535376","24195381_900207122","20156480_735622839","11588063_418447366","13620613_499367929","9114062_316832330","240795_5783755","17913843_653750126","41288862_1575082409","10559621_374199963","45398662_1732523628","47317651_1812678820","43780110_1667434541","23063029_848760035","37366368_1402353368","40753800_1549750897","40769841_1550602009","34560875_1295698627","1243215_36581059","27411295_1025770978","1975315_66481818","27439741_1026405976","21459824_778277323","28715961_1080813430","11045936_395380201","23733495_873804769","23096438_1284949042","27154878_1024553026","28368038_1070206689","37357163_1410409489","24504771_1483685854","46410518_1772664641","8028399_283305358","46543623_1776096240","48189581_1868373889","25932779_978985427","6880330_244627484","4006190_155785840","5322650_184603154","17697774_645519502","15930883_580792582","11283495_405339773","37459126_1467609565","35744333_1336334278","17098480_625877675","49502941_1891588323","46918678_1790971359","2154919_73736049","20251149_738585382","24264513_964415881","3988828_144195586","26294078_982009002","12962917_502856169","24429606_922390529","36742456_1377435805","47406598_1810142439","1210772_35651988","11939953_436726196","9649103_336219104","41704556_1589488973","42618882_1627694979","37751826_1419416611","28373796_1062780980","36456674_1363113382","18903040_718635133","8896619_310041160","29702842_1111799133","4585207_158107413","11973086_434390048","31135616_1165806317","33277766_1249814933","26495191_990665688","38293274_1453360892","35206464_1317427610","49550116_1894292996","24256057_905182494","5078422_177372416","21693053_801878609","37393621_1403566783","24577841_910855120","9520717_331183509","8596627_300479410","39190749_1483326111","10363182_365790941","8379585_293511545","29604662_1108085406","43046085_1640305954","32384748_1216493862","43931504_1672881472","14267600_522051691","4039979_141788590","10123082_388756558","26750195_1043850082","47054971_1796378035","5727254_199379072","30687468_1149295820","26716665_1017054266","3585654_126625041","36375541_1360190495","1154461_33067850","40684807_1546883728","11808899_427464105","28352781_1059774388","2194895_75273231","36945320_1383705053","49329076_1885077731","19878420_726589148","10934329_390593285","39330555_1489358133","32355659_1219222880","39583750_1500590726","3065146_108108160","38680083_1467190401","44370529_1690073399","9270860_322077333","37123257_1391710489","7740438_278427951","6909592_244501619","32565917_1222245311","10575211_374947105","16840896_614053180","37818030_1422196797","11255738_404308569","262273_6059493","45864524_1749813458","9921199_347682717","47449099_1811728682","23259962_854361016","27699080_1041650909","4495604_154904113","46772796_1785203487","5116617_177290442","8307348_291198718","17776014_648556644","1198404_34929835","19073568_697728922","44536739_1731477550","2407404_83598714","2503767_87472017","7000406_247834195","26283469_990148794","22837266_835784160","7626851_286755480","13177333_482316518","41345516_1573932283","32485818_1223292149","26270261_981010432","9114363_316842346","35272742_1321534858","14348056_524657160","45539957_1780035765","36475614_1363895043","15458410_564056126","10971061_392146989","6572580_231414850","46639130_1779888464","25361788_946903107","34242250_1287746902","35074740_1313870070","36317254_1358001931","7127338_252582945","46066108_1757807210","1001411_26940182","17452970_636287724","2357213_86565844","21059174_765102321","9414242_328830311","19652516_718381137","23539581_865672743","30161468_1129331448","15412280_561746195","49104658_1877423469","19539176_714211105","12127363_459039778","12796072_467533245","22155794_805631210","4170474_143899469","1271397_37581947","40419915_1537353315","30664207_1153523994","28570542_1068102960","44320205_1688015467","23766190_875179886","23834543_877772025","43090061_1641821433","46369870_1769368949","2928606_103568868","44952531_1717100425","28501799_1067634933","24651931_913804036","16926175_617025471","41571354_1583578352","41495858_1580756820","6574672_231517860","35332779_1322013824","35188599_1316875736","21235974_770671016","24188504_899917806","39310697_1488574384","31550099_1181284884","34375952_1289275749","23248377_853198356","11464619_413165874","49277220_1884668680","35402090_1324869253","47117116_1798941277","11086635_410990877","6082050_213923877","36347287_1363618810","46233276_1764391716","6546845_230473061","25459893_954290251","30736076_1151066768","19702610_721745935","41823500_1594389039","5224493_180993438","23324483_856378254","18873597_690246374","434680_20276200","37075084_1389274441","40504211_1540753322","18754917_685831805","20119246_735416252","36927867_1383002573","5143110_178096116","12807119_467931555","46706902_1782558246","3351292_146772797","14466138_550225462","40885918_1608723845","13836183_508800339","36909801_1382135900","2072862_70553379","30428871_1139669928","39692927_1527272802","21366323_775072788","30046504_1124867884","24983090_928186262","32767435_1230459352","48484243_1860658887","5100346_179181205","2552325_90826692","27949268_1044471415","10361005_367850355","18698531_684060204","48759652_1863923075","42781591_1631167170","24500829_906879832","11692238_436540221","47479021_1812947317","35009187_1310719729","47899351_1829687527","43724685_1682416769","18774860_694028750","22389543_823584885","1531898_48258659","37762668_1419921872","45898079_1755365402","14838331_541511800","47512253_1815230835","40077066_1521572867","46306518_1769756661","15262542_556503120","43927961_1672734876","14463867_528389994","17705122_1212065398","21428940_778243873","40345723_1533373621","37935105_1427242723","13938264_516683459","11645872_421410725","2453554_85452107","24666018_917202326","27644801_1033372845","46124438_1761083297","31759452_1189660300","15255162_556204919","11257659_404354496","2771828_97623707","28203913_1054040555","14109956_516634480","47506065_1815607191","37817920_1427740702","27746871_1038215184","2968505_104386528","2219493_76131763","48809814_1865623586","30522732_1143318758","8336088_292207211","30473320_1240282344","20836632_758290232","47047746_1807001377","45552554_1737247361","32869319_1234300754","21694289_787033823","21660226_785708217","28561313_1067752044","4659283_164522769","35441265_1326281232","33064370_1242617058","48343531_1849349891","6216374_217837790","9224036_320523946","41253345_1570585340","11577141_418025961","20744843_755115731","9631032_335420105","40501199_1571312501","24340649_916632952","20176642_736264567","12681532_462949407","48256761_1844594780","32710530_1228193607","27079180_1015189485","20979858_762766583","26248199_983116343","35090728_1313385762","23419716_860267716","37473424_1406966542","21346086_774604202","49504225_1891613861","30067252_1125719939","17447765_636070626","42741417_1629868020","13674710_503354158","2679894_98220186","42023840_1619559052","46814429_1786892003","42067334_1604691115","1222879_38653297","7782103_274410341","605278_13840532","18943392_692797066","11386748_411792992","20223287_737624015","47386058_1811315430","17812997_650022486","15059706_550154323","30457744_1140908578","5737212_199882147","29182851_1091803169","43761156_1666523255","14185223_519226682","39717230_1506437023","32078369_1203273819","46250827_1764961061","15338226_582333533","11678269_421942129","31926373_1195938446","42011549_1602267431","14181048_520315960","10524428_372753990","24416973_903276287","27405095_1391122015","38794062_1485527973","10131740_356229539","2209620_77168413","32239461_1208972512","4905330_170186794","10989944_392960785","37682555_1416125528","76860_1606654","37605141_1412720840","22286356_811492725","23288108_854835430","11862399_429744062","35855986_1340355212","38752441_1463672725","16359712_599704317","1182619_34165637","33937347_1315220984","46547474_1776270801","24045846_893254066","22612452_825801061","16905891_616335446","44985343_1714320079","6224285_218169923","19537861_740879812","10982015_397392177","45722932_1744322034","46014898_1755580807","47294210_1832341601","17471660_636956981","11850835_429192917","7802363_275116565","47557350_1818044519","48596679_1859299364","45408804_1731561612","1932803_67372902","35945129_1343583793","46797688_1786478767","36448079_1362762563","41887099_1596966273","37339692_1401014368","12196136_443611264","43332992_1650603156","1804772_59758359","29179092_1091661617","1419387_43747057","29156676_1090915166","13867215_508503993","11676193_422010439","22847818_845968961","997388_26813371","16210274_590955785","8746840_305324580","27839940_1040378890","6123544_217158971","17698412_647916141","27197223_1017364329","1302061_38840637","26629234_995585025","17664143_775253076","48723302_1885067762","36937879_1383397496","25730114_959573140","6469489_242472299","36058485_1347830433","46810341_1786818787","42063799_1606075056","27583100_1031251985","36065866_1352754059","9832089_343907099","24447474_904463177","39911854_1525616380","45185871_1722408455","3046943_107028244","38499105_1457506334","10766144_383361614","36777570_1379094922","23794838_876223230","15341249_560626317","37962997_1428484737","24336791_899800360","20206359_737083580","12655233_461857139","31775149_1199062133","35499524_1364019786","15500613_565025672","35664623_1337629347","21975175_798005487","20867993_759325848","8437105_295291335","6126341_214360643","8863527_309654200","15482648_578046169","20699588_753516762","9971522_349815025","45678015_1760766628","44427751_1692044723","47146327_1800095220","17990179_657238911","27611077_1032246929","4932208_170848569","30797944_1153354750","48401944_1849879304","1404481_43046728","38193330_1438826852","36768804_1375991238","16906068_616334159","20364711_742284268","38019231_1437564645","22826101_834968625","4361073_150349890","9139549_318435580","15902656_579718242","17995089_657171912","38563774_1455316026","45037637_1716462832","29543888_1110201573","6556171_230802626","319191_7536329","1374026_41844475","40931947_1557469127","1381142_42120890","12974822_475454948","45877557_1750890282","15971298_585356349","11643046_434047472","4891568_171629419","46350569_1771595524","42613539_1625221709","12413234_455571705","32373755_1214340645","40039867_1520161836","9225302_320578383","1213867_44496601","34044455_1278719844","2217931_76068961","15708465_577854233","11561807_417232277","38473036_1451180536","36578282_1367807145","20343579_741613848","19514795_713266972",) + var filename: String = "" + + lateinit var fileContents: String + + @Setup(Level.Trial) + fun prepare() { + if (logger.handlers.isEmpty()) { + logger.addHandler(fh) + fh.setFormatter(SimpleFormatter()) + } + logger.info("Benchmarking ANTLR, Processing file number: ${filename}") + fileContents = File(pathToInput + filename).readText() + } + + @Benchmark + @OutputTimeUnit(TimeUnit.NANOSECONDS) + fun measureAntlr(blackhole: Blackhole) { + val antlrParser = Java8Parser(CommonTokenStream(Java8Lexer(CharStreams.fromString(fileContents)))) + blackhole.consume(antlrParser.compilationUnit()) + } +} + +@State(Scope.Benchmark) +open class GllRecoveryBenchmark { + @Param("14032585_516614449","23994681_898810829","37101673_1399232436","6205548_217411567","2567500_135221580","26375825_1002865961","32900055_1235347381","18260055_667305453","44180763_1682824831","13770384_504745021","2702736_94884261","49123149_1877552666","34043419_1277924737","42386067_1617181307","46044461_1756727857","15235194_577364081","10401182_367359496","8383839_293638316","31888163_1194402894","40879792_1555206827","20462736_745665883","8832616_308018249","27151556_1015634282","4234547_145977831","3678398_128763544","30566883_1145190267","39758500_1508082355","2212886_75934168","13697485_501983091","18636474_681527769","42345481_1615557639","48423156_1850708889","2203298_77116149","46104673_1759178220","24051981_887335257","5914594_206527376","23538442_865441635","2798933_101467001","41437037_1577888889","37740815_1420072604","42145027_1608087466","7277363_257926123","26813604_1123555794","10303550_363408086","1276265_37767456","39089518_1478979392","26452060_1007479131","21125576_767193023","10539839_373417275","37354606_1401755257","19812868_724341129","1254315_36926861","4495524_154927023","27542734_1029867468","12467970_585500935","14745131_541032872","11102340_408168235","43746724_1666094162","35143319_1315142795","36854485_1384748997","46671895_1781546607","12634139_461076692","13606347_498597341","2114489_72166941","48092030_1875818509","34555035_1301895570","2253643_77570060","36537754_1376267456","40581595_1542530326","22377301_815252532","48631853_1859167895","49286204_1883692570","22807757_834195621","6421698_225644501","37952147_1427926535","7778055_274280044","3039524_106797694","31033000_1162406475","8773075_306085641","28100733_1061975890","39538162_1498683675","14317410_523758466","35822277_1339143567","26760179_1008825511","7379020_263549919","27199602_1017452207","38083666_1433877665","41035980_1561711021","37494133_1410230970","1670322_54178120","46525281_1775381431","3889612_134895079","27942864_1044342483","12770800_466451028","1692415_55080761","21982667_798338656","43498530_1656755285","28442884_1063094250","2222194_146344952","17643132_643728845","35830397_1339407387","5519479_191988146","17706871_646514850","7129442_252733531","39597357_1503072885","36304420_1357504166","5856744_204290134","12268323_446473327","29994206_1123001795","17174449_626153509","40734349_1549062329","16732569_610006438","5929428_207739384","45153516_1721244569","7219906_256116890","5590187_194628000","37746353_1419242560","22080277_802468954","14060914_514944448","37522402_1408940219","2040979_69237261","9479231_329610943","46185881_1765225025","43188837_1645327377","22996332_842363630","9056200_315058909","1940747_65160008","49038047_1874199796","11412004_410835576","45494222_1734910712","17136987_633448536","18359430_671072384","51062_1046501","43207049_1645982636","10206664_359325128","16327805_596571061","35862733_1342712619","23952779_951257173","1044491_29997907","37001209_1386188141","16609481_605504644","8293143_290749925","34612088_1303889179","46541430_1776527223","34934233_1308412560","5519153_191950698","24163448_921835113","22490783_820243142","37455682_1406170689","5663561_197087373","23228465_852257328","24977760_928375777","4584701_158096701","4554980_158174410","38190145_1438725541","42335103_1615259581","30494756_1148984974","871822_22044242","3629597_127330529","16276686_593522731","8956272_311874142","11584606_418073237","40547139_1541222371","15444366_562964888","8073474_284382416","12949451_475940773","1312896_39258805","2138508_73117603","30024829_1123999680","50190_1021069","19660211_719458755","11967859_439033608","12992293_480521962","16354271_596234812","23475959_913702154","33202112_1258773470","31916781_1195546198","30390418_1138120290","41159575_1566638844","5001055_208346382","11815307_427733906","1190299_34444356","29108187_1089031084","31585010_1182593594","21714655_922462715","40416210_1535898741","21825937_792001857","4473330_154106147","22767022_835411287","22681544_828885626","38922623_1471208367","1684825_56051090","48814347_1865817364","46466007_1779312168","34528527_1298814866","37082486_1389748213","27674410_1034532860","40888509_1561865756","42191270_1609800777","1070020_30682542","13304347_491821013","10962352_391767259","1721047_56371820","13821505_506533208","10943274_390988108","48755877_1863930410","35937919_1343345438","10091193_354525053","39121708_1489709623","41352252_1574214693","12454740_453864748","42921668_1635768371","3412031_137845152","12759538_468408265","40577240_1542420553","36636993_1370237641","41671513_1587895355","6777804_239460436","9498372_330390829","35657368_1658586609","21559783_781912388","37772035_1420382289","40389052_1534864882","28900452_1080965690","5181478_181904393","1662579_53913974","27919361_1043519438","44507651_1695364358","5002847_173150916","35647298_1332932098","11866530_429780260","40799541_1551826425","1878318_62625937","5771639_201045828","14828846_541243509","43772232_1679236679","5004158_177495161","43585036_1659796846","22391407_821021811","42252285_1750316359","40352475_1533125020","22787815_833407862","28885504_1144430075","39300475_1488037474","44637849_1707818141","44759203_1708069234","35642082_1332782672","21975267_798031604","228034_5348091","20534436_748165418","40956007_1559611293","46741139_1783903693","34525040_1294552347","23639523_870078703","20245045_742251831","15037924_548590114","3428288_120386125","39511066_1497290582","13644580_500082026","16504101_601855616","37617247_1419478023","20794910_756773732","6317091_221757433","39781326_1509095169","33302359_1260250935","25498279_949865055","10580767_375225260","9936530_348381631","7709878_272111119","9008149_313967678","2150859_73502769","39851655_1514757309","14900785_543464824","38596827_1456759408","40501615_1539263511","6390201_229901502","42246912_1611790175","26328364_983456346","40903335_1556198944","7745239_273132718","40282189_1535829169","37151525_1392746729","30276123_1133746683","23815247_892198660","1423982_67244612","22788624_833396891","37010668_1386568707","26942404_1007851114","40542978_1545853101","13369753_489548268","20960225_763526337","15994523_583005856","36776020_1376332628","7696770_271745949","36161892_1351771917","3220872_113430599","38704578_1461692788","25485788_955297387","24936374_942129999","11796249_441216204","36177913_1354133223","33591682_1261278997","19101433_698707472","15682832_575503033","44970163_1713821464","19216068_702709875","23854516_878661536","4793526_165687734","6206150_217466803","41389684_1576072299","31981308_1198249607","18806089_687770677","26160736_976928084","22406969_816557338","9985671_350396941","35336032_1322170355","36273940_1356177068","28539494_1066874590","1849136_61491564","49139325_1878123017","20536190_748237186","1884823_62861257","47702961_1821766880","17639771_643442115","11772040_425790935","39686525_1561704631","41879695_1596798701","28306024_1059025094","10390480_366972163","48345284_1849897422","39016801_1475531023","16034031_605418494","11615198_419284506","1741244_57080128","10246882_360999031","31625403_1184231957","40418556_1537808273","5897255_206915280","7837971_277314575","25089513_932533092","4784049_165406296","11083787_398828344","45357515_1729580639","9009074_314031072","18429375_673818324","44277849_1686420948","2146683_77644181","18479646_675539569","35332674_1322010559","1563916_49646095","5821791_203045947","18138322_662748851","1565882_49721418","41337732_1573740212","7125435_252846287","17222436_627909779","47229678_1803247128","47462737_1812280898","10122585_359935574","47828083_1827239668","17405290_634480320","48532746_1855272923","30212143_1131265600","30815377_1154142583","28344272_1059530945","267431_6177313","11649740_420652584","28217171_1054618597","23043408_844404355","23096072_881601576","28986950_1084368157","24601991_911540142","11407132_410625235","44905101_1718580880","8676791_313646765","9338392_324550666","14311931_523699900","22207138_807889618","22836533_835354152","11959559_433546629","40096085_1522415627","12175720_444979101","34969954_1309478506","38286863_1442678153","23473208_862567879","21022230_764084497","38622851_1457892593","4707453_175915663","49124396_1877589520","16953658_617985664","2831822_99630754","34812941_1304317829","49312308_1885621092","3519005_123630450","36154282_1355468183","47895606_1840135694","1545449_48897711","18873910_693741673","22036001_800619994","604529_13831332","2148272_73431878","38332785_1444876067","44796843_1731563673","3658363_128219477","23568492_872118367","28478983_1064951023","21474988_778866739","36900272_1384277855","14403870_526325009","19513269_713216254","22084411_802660651","48179344_1841305216","5295489_183615403","28471625_1064204080","4951685_171400707","28731670_1074474201","46619556_1779127064","40699340_1587812418","5380671_187433425","36742101_1380381221","28182334_1053269228","44470099_1719401832","39994037_1518262180","28049769_1048316939","40484897_1539228655","16251988_592646875","25082089_936928450","19796563_723699507","45073783_1717836308","35425738_1325190812","1197161_34698346","1079718_30051892","42592068_1624526221","32248408_1209260737","11698625_424855398","1721305_57413864","45672459_1742260779","40076439_1521627729","40584759_1543208877","22252824_809966777","12839621_469356263","20467366_759140781","44493313_1694668449","35071654_1314479085","22516905_821472079","29933664_1120547319","31036911_1162254206","10139534_356645253","11082820_396859915","33462985_1256437027","37086733_1389868364","28471320_1064250200","31458501_1178677941","959458_25455156","17564126_640460186","18383409_676258199","9600860_334273152","18275438_667885322","38525359_1453754271","11023819_394471458","11857249_429419795","10926053_390312658","9802109_342769250","42815652_1632221102","11646202_420541690","12381706_450933492","38241808_1443147292","34678744_1302180083","6571552_231364555","41872657_1596442876","15740242_593779962","35713826_1335438134","9484453_329824118","24475158_1278505796","28162556_1060507118","22688950_829274767","39252113_1486064147","2085440_71015699","8960064_314559106","49257277_1882412897","23338140_856992586","15033101_551445216","1078315_31597280","20013091_730860966","36094382_1349767240","41748692_1594475403","2810143_98799915","23961237_883426905","28411214_1061957041","15799576_577700223","45926614_1787636853","49083376_1876039825","38212667_1439865933","20003610_730573924","1969564_66243819","4909874_170245167","4238176_146089696","43238559_1647070934","9470313_338179118","20895721_760175493","24737950_937037702","37262328_1397548853","7167969_254086309","22607848_825587799","44487384_1694447611","26504786_991044310","14639691_534434533","13470184_505533833","13431444_491974436","49048959_1875042069","38901991_1470282424","38042025_1432118299","29671062_1110658112","26945699_1007979714","41254504_1570438656","37612146_1413046017","18391029_672456341","9771196_363019395","3147026_110706816","43784475_1667462391","11335444_407460135","6718606_237001259","37658800_1415146245","35500108_1327776688","40337124_1532607963","35190816_1316798625","14109345_516584701","35678784_1334099188","49326115_1884979220","21830930_792222381","17674399_658621718","27491977_1029050979","32033329_1328601378","43958406_1673837980","26982737_1009382720","23134891_848104671","37669101_1415547213","7876128_277488838","19849538_725590233","22882984_838139888","16778354_611940589","23622223_868973326","16127641_590104230","36642814_1370620254","41621489_1592732693","36828663_1378651395","2994253_105305024","33818392_1271178288","40695231_1581710921","29406723_1100463768","13652652_500396289","44260282_1698798267","12111291_440059435","12044022_437097842","12853719_470045039","15817525_576716923","7884539_280301655","4233909_145973453","11837715_444461252","28180136_1053176664","6651213_234429860","18914671_692909051","18148702_675343535","2123320_72432446","25861111_964930180","22141579_805044423","11196222_401854551","20267072_750914697","39168548_1482452714","13874115_508574273","30192908_1130647956","9757726_340888717","12496329_458221385","45631673_1740462861","20264139_739000824","1771938_58305193","29858364_1122085724","41472428_1582664169","4060674_146643474","22390540_815825605","37258395_1397462644","36175887_1352290618","22675029_828610673","21444548_777757920","23096072_862253744","33855646_1276980613","16843908_614133600","16056816_585442918","34000073_1276276594","19793285_723598488","33153081_1244981667","15149802_552519370","27495175_1028282788","35164665_1315889041","5062799_181825045","43561008_1658967181","23204703_851122356","40912575_1561730204","28386486_1061084922","36238268_1365227176","37009392_1386492004","37459079_1406281153","6266493_225337789","42067529_1604925874","1747807_59976697","3462841_121615234","19472758_711848494","39559404_1514537955","47362259_1808243186","49056569_1887684476","10529588_375160623","43409921_1653433458","37185550_1394151764","38514863_1532994723","432925_10357884","37606463_1412806455","36679428_1372545608","24178034_893218308","43376655_1679778212","35627414_1332236807","11343409_438294472","1539240_48626998","13933571_510665785","41749225_1608227466","26259433_983730029","48827514_1866347163","42843041_1633144368","47817621_1855102229","48568174_1856676911","17192340_626757845","2530485_88510785","44366352_1695697544","24362217_900774196","47590757_1817507553","33135117_1249104362","38282073_1442421466","38356042_1452902640","13053204_477728211","1452751_48102085","4853130_167893359","22350781_814305773","31315742_1172470780","24969792_931334426","3653238_128283864","44324640_1688180738","32039670_1201443678","13351027_488864947","14067332_515105539","22629584_828767659","11003213_420480441","48921217_1872210144","42580899_1629894147","22935244_839951722","29446948_1102113082","24992139_928462548","22749479_831714974","35066861_1312600808","35251532_1319025925","2268020_78199411","44751617_1704903501","26075993_974557900","41979723_1603784813","23194857_853577119","6189627_216817295","23737694_886714371","37066746_1388919478","47980212_1833138636","47037395_1798203228","42345849_1740729334","15570753_570998257","39788950_1540549269","25727127_959493528","29653764_1110002905","36287149_1356752263","27361408_1023556009","22535963_822410948","27645915_1033479847","23350877_857544928","1639278_52950687","4960435_174362415","24628631_915538259","17260739_629143176","30684431_1149131410","33882331_1280612970","10226778_360085637","35137134_1315003110","26645892_1004185367","4596122_158560736","849376_21209231","36006689_1347416843","29979921_1122374216","25787422_962091064","33472129_1256786335","8382910_336222845","13319594_487518761","31704347_1187436082","18462396_696730626","26957371_1008462019","19796228_723692334","5757937_200528044","13679251_507876139","36032672_1348245953","40679512_1546631339","9694345_338137844","24213007_894572088","48278304_1845423133","29131015_1089780045","24081707_888712087","1049113_28850165","48903092_1869193330","19926396_728201228","17808349_649860875","39521834_1497813673","29468654_1102791235","3971743_137509130","3387946_120306608","8368826_293197245","6873879_243125979","10383324_366677203","11572527_419968425","48483356_1853078429","6660346_234735495","4954550_171915839","32159727_1205690492","5903899_256049779","31939682_1196549894","49532186_1898868952","30215108_1131352520","15716344_572999679","29457134_1102604097","6264123_220017786","45365257_1729862784","23918385_881303570","42977669_1637658058","42321840_1614717930","17894203_652996529","49557424_1893537523","24101641_889642729","4498235_155018402","6390283_224565723","45666991_1742062671","38721157_1462172749","21808954_791417320","47369229_1808577719","33693151_1265001167","39739682_1511278712","8753583_305481633","36235090_1354548201","11272890_407444882","9448222_328450542","1320214_39504487","2788951_98144129","37686470_1416309291","24559538_909587432","23037773_844313528","48479800_1866437440","40246668_1528605784","13322261_487605288","49326558_1884989824","12928036_472964614","33227529_1247908199","298854_7029160","15852699_578669618","4745628_164082160","10598344_376069675","35907340_1349754212","2323123_80392100","4334791_149508606","7906592_280602663","40197020_1526500027","47558461_1816297617","49481353_1893766240","10528259_372886896","30121216_1127800590","33962879_1274961539","36147180_1355051144","34874831_1306430544","10582078_375283000","41566778_1583336977","44429011_1834438688","41148307_1566639884","3111653_111077159","45597473_1739126639","2890818_101617866","18727220_684918716","26994502_1009907728","2130638_72703580","43341057_1654389985","26156971_993603492","11933643_432588045","9593476_334134555","29499906_1115610778","35158744_1315707884","40057548_1520752327","23524850_864896080","14101365_516330180","41312243_1573018338","43312947_1652315916","13457948_492876494","41650551_1590633223","44263969_1691597626","48896652_1869027254","26686874_997632782","32376027_1214418269","45619075_1787225805","6233771_218741767","35187464_1317029177","41259382_1570688911","2523648_88164699","32391638_1215146297","10131136_369377030","22790126_838885469","18501135_676439914","21430110_777216280","48809094_1865794966","44859064_1709265472","43731184_1665500053","29483400_1105301765","10270379_361893425","30727797_1150719892","26365732_984913818","38366963_1452897381","34070170_1279452623","835052_20693935","18118994_662098345","36654996_1371116332","7703741_271908154","5530084_192353359","30744059_1151315962","18071997_660073101","33862663_1272156889","26082605_974000911","34717995_1301234731","1499064_46952642","33031513_1240443731","34985356_1310060132","27814362_1039538435","25096661_937779063","10475909_379313320","24215964_894660108","37175570_1397211040","10669352_389042402","46032435_1756370951","29784936_1114886832","1141996_37645162","23488180_863190831","16761896_625241393","30516571_1158708906","8366926_294402297","20307302_740334316","17344968_635953472","39135805_1481652617","44584781_1727663650","13838016_507196882","7127354_252579169","30399910_1145222315","7877637_277526806","10582122_377134782","30986492_1172080741","8552197_299049971","3957237_137951692","28122567_1095682856","35932400_1343158558","41929597_1598800982","26533613_991782023","23466781_862342541","11603961_418817369","18485331_680701652","15591782_568425062","29539858_1112262764","2988933_105119706","35546679_1329399427","15172778_600068993","25489430_949436245","1838974_63442638","5055369_174862164","11159527_400152451","9573552_333214658","48980685_1872151835","14636080_534321455","7819732_306289986","39573435_1499970795","11554977_416917692","49136603_1877981266","37636276_1414198456","15931849_580814593","39188916_1486802039","36724858_1374062297","39934679_1515824284","19848934_730701504","4152106_143342917","5458202_189666509","36934516_1383343203","11300006_406039829","14504630_530093364","4601642_158803965","5255394_182015760","14011604_513276700","48800531_1865282411","9649350_336230707","49353159_1885931520","3322265_152860600","43048704_1641732360","10192454_358812012","46654840_1780616256","26902022_1006306552","13981866_513841453","1795605_70406850","23449744_861476269","10763582_385601326","28242365_1055491597","41986757_1601198815","24213436_895764628","41225614_1569468114","17080768_625265320","47611925_1818541662","14734179_539245878","12863185_470300763","46617728_1781279798","23518228_864484920","39565139_1499662186","10396922_367205418","24668146_914351061","30831002_1174276506","47071332_1797069990","33708851_1265601784","10274663_362169921","44472232_1693924024","5860568_204472849","20285357_739629158","36545597_1366721763","31285444_1171484025","36452495_1364906780","28557463_1067575687","19304990_713025632","4953175_171456802","48497200_1853687319","26454180_990638075","12966869_474366789","46754576_1784505791","26806099_1002427423","32658995_1226058166","1172365_33791936","32884209_1234795165","35318834_1321537139","21433629_779748681","37396397_1403685243","16686145_613289811","1415535_50350394","45535446_1736795883","12710119_464108433","31851655_1193128623","46468264_1773253694","23509692_866473364","36567331_1367497624","26800361_1002189140","29057255_1086996002","22347761_822183475","36387065_1361955665","25354035_944245595","32096740_1204148656","36857207_1379880448","41177957_1567517257","44254145_1685631139","44349184_1712004568","5899672_205933835","13183853_482565766","478615_11111585","26774265_1037917254","13371438_489597244","17056600_622284838","26718049_998863301","37960964_1428377894","39196294_1483635379","11636239_420102910","13353275_488919878","37199309_1400534240","28125098_1051081906","46894102_1794637895","13465833_497376069","35810263_1339697843","32896176_1239447059","17530256_639101176","2148116_73403951","6447978_226497861","17432188_635750202","43319522_1658004245","48903980_1869505376","22604980_825497112","15981271_582570916","12388754_477087071","36982007_1385372980","25417139_946423544","23172745_849733703","1924610_66175897","12102647_439580766","578230_13404470","11637905_420141534","17590204_641526303","9059546_315154286","30833926_1159882607","2215078_76029115","6201184_241716643","15341809_559431666","37205435_1398004181","10155285_357225781","41262965_1583937703","19985852_730190098","28377783_1060733122","44274485_1686298202","42418660_1618263760","5970254_208568790","36475915_1363868099","36718594_1415591202","20678098_752773533","21429038_777228991","39109712_1490334982","24772461_918842430","22919021_839187289","46022763_1758737640","18725041_720960042","45881361_1750415636","8312710_291407377","6865140_242802927","38130594_1436202406","5477469_190354642","5578016_194077510","19636905_723334460","10150168_370937386","4152289_149016529","40743875_1551694955","2156075_76848758","16736729_638397335","29320076_1097310300","38240324_1442385537","26009224_970973477","2709840_95244518","23953282_882895548","649500_15832385","10903176_389268052","37480444_1407319562","28019691_1056446968","33324801_1251274859","27592464_1031552471","22598896_829256683","21983190_798380554","10619429_377087409","17831778_650679117","38665203_1463650717","3204643_112917834","26277664_994902766","31629456_1184402104","22891928_837967292","33558510_1260095378","44063330_1678014486","18818726_688296931","41176397_1567301442","38202963_1439193777","39281667_1487238009","30210847_1131282618","48613237_1858453188","45952605_1753018986","6934872_245404874","31315721_1172484980","15010436_547476441","23181426_850317772","45500284_1735184861","25791237_962231070","22768937_835140987","6116979_214067176","38507187_1452734231","33587641_1289861820","8695253_303660946","34494113_1297168329","33494436_1257634189","35670953_1333809400","5084978_175925684","46336623_1768174970","18146939_663034648","12186430_443008589","34336820_1288151064","37339421_1401067274","47944684_1852403157","16851266_616253475","25791350_969454993","21967727_797667705","23664824_870869499","22234818_838323391","25517596_1044227986","13118027_484876578","36736980_1374710891","11817408_427805193","41359557_1574501012","5272812_182744369","25345116_963549644","18266230_667525878","47784772_1826295130","18679155_683080652","45830733_1748600236","30183483_1130167230","10934103_390612214","11335179_412570422","40417958_1604507055","35286712_1323261151","36547782_1373079960","33014000_1239765195","48125411_1867356628","30746834_1159558838","26034487_972023247","30490714_1146936287","3295478_118674908","29469428_1103599657","39949604_1516399501","27238461_1019003903","38256164_1444008716","9772750_346099219","20867790_759274846","38324969_1444412063","22971897_841422749","29140415_1090193272","21373520_775332264","22723809_830547953","46913420_1790830537","25809755_963041010","1458189_45304424","31421882_1176964041","48577773_1856978474","13452670_503028326","10898105_388995275","25770319_969008505","47247307_1844764015","1287836_38243801","11153176_420338693","22798488_833846130","31333120_1179884622","39281149_1487273861","17892932_653029302","6638372_233898997","48823201_1866148878","13850241_507721571","35032611_1311452832","49076314_1875782221","4527987_156127222","36840693_1379140611","41658762_1587364524","43712062_1664582709","32162212_1210707520","47650463_1819970341","33120821_1243835453","38608068_1458894269","11439956_425353562","1447866_46645913","33069531_1241774797","17602955_642846284","3094943_108715623","47470303_1812968440","39471668_1495469230","4866654_168371872","20258239_740092953","917761_33653544","27149660_1015556926","18772981_689870557","24790150_919664308","23663587_870820668","1847614_61421955","36963135_1444944930","36101821_1350177554","19585108_715984180","13919646_510212617","10628963_377506220","24966607_927482774","18693817_683594653","20653851_754532016","35163604_1315846751","22777317_832955301","40969485_1558930078","20524212_747813583","12242698_445453843","36245340_1354999657","19369413_708265097","1862761_62085791","198004_4848477","14891628_546341353","17547844_639762566","38294959_1446201165","856051_21443571","28646403_1071259127","21669307_786089080","12968796_474494113","32886082_1238328066","31609834_1183551228","44209058_1688294008","46001866_1755271945","25796984_962593468","37483697_1407598581","1818745_60272910","16656929_607211069","39036050_1476470337","20743981_755090407","41782205_1611290125","9524404_333628946","28651942_1078477929","43584653_1664913001","10007658_351327563","40613216_1543834943","39240768_1637166162","36225153_1354611563","6564575_238813989","9851064_344652980","2019490_68543429","16377824_597127826","27042664_1019730814","35276007_1319819580","25053898_931091765","36234277_1354568647","37982534_1429472337","5131095_177666735","37108592_1390873078","43781504_1667298596","984011_26280371","18511882_677246989","44600058_1700525468","3314468_116729708","27941949_1051240798","39404753_1492519363","23194036_850670220","22646655_827362845","542209_12530517","32524331_1220511924","20030096_731377195","5090148_176135052","5343040_185868895","46005568_1755313932","31452839_1177719067","48300895_1846197169","28052873_1048704034","20712447_758942939","39198603_1487136272","48244814_1850916413","35781590_1337628951","3158902_111195627","39272920_1486848464","48452628_1868792720","26141786_976229891","40043748_1530342293","17537123_639349657","26630757_1003710225","2638325_92357477","21929219_796124388","24568842_910270308","8603388_300701822","9338215_324546126","15257863_558579991","6993370_251209488","31342299_1173624114","32216537_1207972301","40791308_1551584872","6012527_210119728","17672304_644614261","34043389_1277894765","3016860_116233607","27761095_1039136825","15383363_564284879","11359975_408476410","48989025_1872537798","11196424_401841236","25364161_944413919","18724632_695171388","23378428_868147320","10376502_367964459","12664975_462455020","8349454_292620407","12460872_460659915","37293259_1398907220","33064649_1241600449","28564393_1083258186","42026957_1605305132","21348622_774511259","9918088_347565822","11922954_433018555","47836272_1827016467","1182436_34136171","46825712_1787365045","44003460_1675588591","22231079_808926594","18091773_660835165","39682832_1506489328","2909950_102374371","11527870_416081663","35810639_1338686547","16541502_608773124","16389239_597532823","47270870_1804857024","1720243_56361113","40358952_1536714073","18636978_681548851","29666823_1110452097","47001926_1794217904","9591338_333900946","16273934_601945501","45773952_1749200231","40407623_1535522990","22232707_809015066","21614532_783940271","1316307_39404386","22776438_832812989","17843430_654279144","42172760_1609037561","12733712_467198255","26527752_991666230","30847922_1155325063","48600967_1858624310","31328633_1176227345","10987513_393077681","13614084_498933194","36516052_1365410163","22434377_817773228","38856979_1468520556","17689664_645263534","6704361_236465011","31975746_1212304147","39651071_1503459046","45197830_1722814238","38750327_1519954154","40568534_1542030942","12032049_436551199","22668382_828359849","40703958_1550011635","21691324_786969611","32816805_1241900042","42875805_1635219581","11172021_400656255","37176415_1393771073","36809313_1377997047","49217457_1882122052","8213122_288098466","25980510_969740177","37357110_1413258331","40669233_1546306709","44796394_1706699681","34665977_1299413130","37504870_1408333734","6018322_210424392","46797534_1786272031","40486424_1538639292","23865217_879540168","21920515_797844314","6355488_235899102","2195959_78094130","48245514_1844144858","7027222_249039736","18332211_670089796","31235318_1169613079","750202_19234046","30032070_1151887984","40403924_1549288711","41506168_1581007720","12635099_494971652","914513_29318632","35802442_1338388768","9783587_341986497","26887706_1005786893","48904676_1869268471","19136814_705283993","43705014_1664247587","49606320_1895307325","29891616_1126329706","47733798_1822938816","5089559_249457764","36121860_1350307928","41302188_1572331392","38400783_1451206545","12821050_468567780","24401397_902627186","23116392_847963747","24335441_899801099","4272563_154711814","17522255_714324224","31041574_1162479241","21351288_774595397","4585859_158115219","34236151_1284658676","28430053_1062706161","2214647_75967932","42036061_1603228846","36064171_1348040027","44114162_1680058840","10996783_397923347","10972921_392223033","5975315_208828902","21081590_765765729","30381450_1137827659","2467034_161582253","10990160_392942163","24434205_903914037","18179624_664311623","26712548_1008659270","43015759_1639063723","17280498_639969634","18658000_685362475","11327907_410807748","443244_10358161","7663942_270619402","43883912_1671833434","38468481_1451037340","16837455_620719325","6067577_216647386","17546494_639721112","49624411_1895899854","38389044_1447582579","13755481_504113182","42607605_1625063626","35215956_1317767456","32151385_1205341864","39822272_1510778499","18347042_670562855","42827177_1632568269","35280540_1328739183","48897311_1869109683","32938809_1239855989","18595127_682866902","12897073_471764266","24660318_914025028","26088836_976628556","11560252_417167174","14336038_535872876","36512404_1367437551","25681145_957426188","10491566_371348960","3027638_111896754","25720849_959195382","17639314_646430047","15453734_563452042","23367480_874037438","7570548_267867240","35146512_1315279791","24444839_904403849","24205954_894410919","5959192_212134268","21509869_780068551","10934610_390597620","32419048_1247787153","10533397_373070746","32431712_1216720355","14937040_544717876","13364741_489327515","38694781_1460919862","3904896_135386486","24976797_934059281","37157949_1393011448","39548403_1498868750","18523257_677212116","953003_42595981","32844984_1243472952","36215604_1353834214","13066570_478223836","11976074_434290596","33827663_1282123100","2747711_96557750","1040452_126196409","47318039_1807150239","16033910_584468599","43122864_1644942358","28873994_1079902712","26668255_1012089415","39133665_1480783576","23520963_864628884","27914158_1043204707","38026454_1434776425","32509253_1219827766","24571308_910313703","2463645_85904962","30166309_1143687668","359897_8795933","39567089_1502293558","16912523_654477621","31141168_1166009539","1406033_43761192","21417864_818139355","2192583_75221027","30988742_1229006980","39373998_1491067247","40282941_1530121410","2358226_81903007","6471959_227548939","6045280_211434817","12045756_437245210","4947760_171282610","9312269_323632108","40693599_1547219440","48141064_1845571405","40586820_1549530460","41980939_1609790938","15490684_564683501","2015891_68152446","10433544_368814928","4863383_168279448","43387695_1652615810","1588668_50709819","26697928_998000825","41824509_1594349839","26892962_1005992100","8785905_306520979","5385527_187065370","3454671_121370158","46211963_1763535376","24195381_900207122","20156480_735622839","11588063_418447366","13620613_499367929","9114062_316832330","240795_5783755","17913843_653750126","41288862_1575082409","10559621_374199963","45398662_1732523628","47317651_1812678820","43780110_1667434541","23063029_848760035","37366368_1402353368","40753800_1549750897","40769841_1550602009","34560875_1295698627","1243215_36581059","27411295_1025770978","1975315_66481818","27439741_1026405976","21459824_778277323","28715961_1080813430","11045936_395380201","23733495_873804769","23096438_1284949042","27154878_1024553026","28368038_1070206689","37357163_1410409489","24504771_1483685854","46410518_1772664641","8028399_283305358","46543623_1776096240","48189581_1868373889","25932779_978985427","6880330_244627484","4006190_155785840","5322650_184603154","17697774_645519502","15930883_580792582","11283495_405339773","37459126_1467609565","35744333_1336334278","17098480_625877675","49502941_1891588323","46918678_1790971359","2154919_73736049","20251149_738585382","24264513_964415881","3988828_144195586","26294078_982009002","12962917_502856169","24429606_922390529","36742456_1377435805","47406598_1810142439","1210772_35651988","11939953_436726196","9649103_336219104","41704556_1589488973","42618882_1627694979","37751826_1419416611","28373796_1062780980","36456674_1363113382","18903040_718635133","8896619_310041160","29702842_1111799133","4585207_158107413","11973086_434390048","31135616_1165806317","33277766_1249814933","26495191_990665688","38293274_1453360892","35206464_1317427610","49550116_1894292996","24256057_905182494","5078422_177372416","21693053_801878609","37393621_1403566783","24577841_910855120","9520717_331183509","8596627_300479410","39190749_1483326111","10363182_365790941","8379585_293511545","29604662_1108085406","43046085_1640305954","32384748_1216493862","43931504_1672881472","14267600_522051691","4039979_141788590","10123082_388756558","26750195_1043850082","47054971_1796378035","5727254_199379072","30687468_1149295820","26716665_1017054266","3585654_126625041","36375541_1360190495","1154461_33067850","40684807_1546883728","11808899_427464105","28352781_1059774388","2194895_75273231","36945320_1383705053","49329076_1885077731","19878420_726589148","10934329_390593285","39330555_1489358133","32355659_1219222880","39583750_1500590726","3065146_108108160","38680083_1467190401","44370529_1690073399","9270860_322077333","37123257_1391710489","7740438_278427951","6909592_244501619","32565917_1222245311","10575211_374947105","16840896_614053180","37818030_1422196797","11255738_404308569","262273_6059493","45864524_1749813458","9921199_347682717","47449099_1811728682","23259962_854361016","27699080_1041650909","4495604_154904113","46772796_1785203487","5116617_177290442","8307348_291198718","17776014_648556644","1198404_34929835","19073568_697728922","44536739_1731477550","2407404_83598714","2503767_87472017","7000406_247834195","26283469_990148794","22837266_835784160","7626851_286755480","13177333_482316518","41345516_1573932283","32485818_1223292149","26270261_981010432","9114363_316842346","35272742_1321534858","14348056_524657160","45539957_1780035765","36475614_1363895043","15458410_564056126","10971061_392146989","6572580_231414850","46639130_1779888464","25361788_946903107","34242250_1287746902","35074740_1313870070","36317254_1358001931","7127338_252582945","46066108_1757807210","1001411_26940182","17452970_636287724","2357213_86565844","21059174_765102321","9414242_328830311","19652516_718381137","23539581_865672743","30161468_1129331448","15412280_561746195","49104658_1877423469","19539176_714211105","12127363_459039778","12796072_467533245","22155794_805631210","4170474_143899469","1271397_37581947","40419915_1537353315","30664207_1153523994","28570542_1068102960","44320205_1688015467","23766190_875179886","23834543_877772025","43090061_1641821433","46369870_1769368949","2928606_103568868","44952531_1717100425","28501799_1067634933","24651931_913804036","16926175_617025471","41571354_1583578352","41495858_1580756820","6574672_231517860","35332779_1322013824","35188599_1316875736","21235974_770671016","24188504_899917806","39310697_1488574384","31550099_1181284884","34375952_1289275749","23248377_853198356","11464619_413165874","49277220_1884668680","35402090_1324869253","47117116_1798941277","11086635_410990877","6082050_213923877","36347287_1363618810","46233276_1764391716","6546845_230473061","25459893_954290251","30736076_1151066768","19702610_721745935","41823500_1594389039","5224493_180993438","23324483_856378254","18873597_690246374","434680_20276200","37075084_1389274441","40504211_1540753322","18754917_685831805","20119246_735416252","36927867_1383002573","5143110_178096116","12807119_467931555","46706902_1782558246","3351292_146772797","14466138_550225462","40885918_1608723845","13836183_508800339","36909801_1382135900","2072862_70553379","30428871_1139669928","39692927_1527272802","21366323_775072788","30046504_1124867884","24983090_928186262","32767435_1230459352","48484243_1860658887","5100346_179181205","2552325_90826692","27949268_1044471415","10361005_367850355","18698531_684060204","48759652_1863923075","42781591_1631167170","24500829_906879832","11692238_436540221","47479021_1812947317","35009187_1310719729","47899351_1829687527","43724685_1682416769","18774860_694028750","22389543_823584885","1531898_48258659","37762668_1419921872","45898079_1755365402","14838331_541511800","47512253_1815230835","40077066_1521572867","46306518_1769756661","15262542_556503120","43927961_1672734876","14463867_528389994","17705122_1212065398","21428940_778243873","40345723_1533373621","37935105_1427242723","13938264_516683459","11645872_421410725","2453554_85452107","24666018_917202326","27644801_1033372845","46124438_1761083297","31759452_1189660300","15255162_556204919","11257659_404354496","2771828_97623707","28203913_1054040555","14109956_516634480","47506065_1815607191","37817920_1427740702","27746871_1038215184","2968505_104386528","2219493_76131763","48809814_1865623586","30522732_1143318758","8336088_292207211","30473320_1240282344","20836632_758290232","47047746_1807001377","45552554_1737247361","32869319_1234300754","21694289_787033823","21660226_785708217","28561313_1067752044","4659283_164522769","35441265_1326281232","33064370_1242617058","48343531_1849349891","6216374_217837790","9224036_320523946","41253345_1570585340","11577141_418025961","20744843_755115731","9631032_335420105","40501199_1571312501","24340649_916632952","20176642_736264567","12681532_462949407","48256761_1844594780","32710530_1228193607","27079180_1015189485","20979858_762766583","26248199_983116343","35090728_1313385762","23419716_860267716","37473424_1406966542","21346086_774604202","49504225_1891613861","30067252_1125719939","17447765_636070626","42741417_1629868020","13674710_503354158","2679894_98220186","42023840_1619559052","46814429_1786892003","42067334_1604691115","1222879_38653297","7782103_274410341","605278_13840532","18943392_692797066","11386748_411792992","20223287_737624015","47386058_1811315430","17812997_650022486","15059706_550154323","30457744_1140908578","5737212_199882147","29182851_1091803169","43761156_1666523255","14185223_519226682","39717230_1506437023","32078369_1203273819","46250827_1764961061","15338226_582333533","11678269_421942129","31926373_1195938446","42011549_1602267431","14181048_520315960","10524428_372753990","24416973_903276287","27405095_1391122015","38794062_1485527973","10131740_356229539","2209620_77168413","32239461_1208972512","4905330_170186794","10989944_392960785","37682555_1416125528","76860_1606654","37605141_1412720840","22286356_811492725","23288108_854835430","11862399_429744062","35855986_1340355212","38752441_1463672725","16359712_599704317","1182619_34165637","33937347_1315220984","46547474_1776270801","24045846_893254066","22612452_825801061","16905891_616335446","44985343_1714320079","6224285_218169923","19537861_740879812","10982015_397392177","45722932_1744322034","46014898_1755580807","47294210_1832341601","17471660_636956981","11850835_429192917","7802363_275116565","47557350_1818044519","48596679_1859299364","45408804_1731561612","1932803_67372902","35945129_1343583793","46797688_1786478767","36448079_1362762563","41887099_1596966273","37339692_1401014368","12196136_443611264","43332992_1650603156","1804772_59758359","29179092_1091661617","1419387_43747057","29156676_1090915166","13867215_508503993","11676193_422010439","22847818_845968961","997388_26813371","16210274_590955785","8746840_305324580","27839940_1040378890","6123544_217158971","17698412_647916141","27197223_1017364329","1302061_38840637","26629234_995585025","17664143_775253076","48723302_1885067762","36937879_1383397496","25730114_959573140","6469489_242472299","36058485_1347830433","46810341_1786818787","42063799_1606075056","27583100_1031251985","36065866_1352754059","9832089_343907099","24447474_904463177","39911854_1525616380","45185871_1722408455","3046943_107028244","38499105_1457506334","10766144_383361614","36777570_1379094922","23794838_876223230","15341249_560626317","37962997_1428484737","24336791_899800360","20206359_737083580","12655233_461857139","31775149_1199062133","35499524_1364019786","15500613_565025672","35664623_1337629347","21975175_798005487","20867993_759325848","8437105_295291335","6126341_214360643","8863527_309654200","15482648_578046169","20699588_753516762","9971522_349815025","45678015_1760766628","44427751_1692044723","47146327_1800095220","17990179_657238911","27611077_1032246929","4932208_170848569","30797944_1153354750","48401944_1849879304","1404481_43046728","38193330_1438826852","36768804_1375991238","16906068_616334159","20364711_742284268","38019231_1437564645","22826101_834968625","4361073_150349890","9139549_318435580","15902656_579718242","17995089_657171912","38563774_1455316026","45037637_1716462832","29543888_1110201573","6556171_230802626","319191_7536329","1374026_41844475","40931947_1557469127","1381142_42120890","12974822_475454948","45877557_1750890282","15971298_585356349","11643046_434047472","4891568_171629419","46350569_1771595524","42613539_1625221709","12413234_455571705","32373755_1214340645","40039867_1520161836","9225302_320578383","1213867_44496601","34044455_1278719844","2217931_76068961","15708465_577854233","11561807_417232277","38473036_1451180536","36578282_1367807145","20343579_741613848","19514795_713266972",) + var filename: String = "" + val startState = JavaGrammar().getRsm() + + lateinit var fileContents: String + + @Setup(Level.Trial) + fun prepare() { + if (logger.handlers.isEmpty()) { + logger.addHandler(fh) + fh.setFormatter(SimpleFormatter()) + } + logger.info("Benchmarking Default GLL, Processing file number: ${filename}") + fileContents = File(pathToInput + filename).readText() + } + + @Benchmark + @OutputTimeUnit(TimeUnit.NANOSECONDS) + fun measureGll(blackhole: Blackhole) { + val inputGraph = getTokenStream(fileContents) + val gll = Gll(startState, inputGraph, recovery = RecoveryMode.ON, reachability = ReachabilityMode.REACHABILITY) + + blackhole.consume(gll.parse()) + } +} + +@State(Scope.Benchmark) +open class GllDefaultBenchmark { + @Param("14032585_516614449","23994681_898810829","37101673_1399232436","6205548_217411567","2567500_135221580","26375825_1002865961","32900055_1235347381","18260055_667305453","44180763_1682824831","13770384_504745021","2702736_94884261","49123149_1877552666","34043419_1277924737","42386067_1617181307","46044461_1756727857","15235194_577364081","10401182_367359496","8383839_293638316","31888163_1194402894","40879792_1555206827","20462736_745665883","8832616_308018249","27151556_1015634282","4234547_145977831","3678398_128763544","30566883_1145190267","39758500_1508082355","2212886_75934168","13697485_501983091","18636474_681527769","42345481_1615557639","48423156_1850708889","2203298_77116149","46104673_1759178220","24051981_887335257","5914594_206527376","23538442_865441635","2798933_101467001","41437037_1577888889","37740815_1420072604","42145027_1608087466","7277363_257926123","26813604_1123555794","10303550_363408086","1276265_37767456","39089518_1478979392","26452060_1007479131","21125576_767193023","10539839_373417275","37354606_1401755257","19812868_724341129","1254315_36926861","4495524_154927023","27542734_1029867468","12467970_585500935","14745131_541032872","11102340_408168235","43746724_1666094162","35143319_1315142795","36854485_1384748997","46671895_1781546607","12634139_461076692","13606347_498597341","2114489_72166941","48092030_1875818509","34555035_1301895570","2253643_77570060","36537754_1376267456","40581595_1542530326","22377301_815252532","48631853_1859167895","49286204_1883692570","22807757_834195621","6421698_225644501","37952147_1427926535","7778055_274280044","3039524_106797694","31033000_1162406475","8773075_306085641","28100733_1061975890","39538162_1498683675","14317410_523758466","35822277_1339143567","26760179_1008825511","7379020_263549919","27199602_1017452207","38083666_1433877665","41035980_1561711021","37494133_1410230970","1670322_54178120","46525281_1775381431","3889612_134895079","27942864_1044342483","12770800_466451028","1692415_55080761","21982667_798338656","43498530_1656755285","28442884_1063094250","2222194_146344952","17643132_643728845","35830397_1339407387","5519479_191988146","17706871_646514850","7129442_252733531","39597357_1503072885","36304420_1357504166","5856744_204290134","12268323_446473327","29994206_1123001795","17174449_626153509","40734349_1549062329","16732569_610006438","5929428_207739384","45153516_1721244569","7219906_256116890","5590187_194628000","37746353_1419242560","22080277_802468954","14060914_514944448","37522402_1408940219","2040979_69237261","9479231_329610943","46185881_1765225025","43188837_1645327377","22996332_842363630","9056200_315058909","1940747_65160008","49038047_1874199796","11412004_410835576","45494222_1734910712","17136987_633448536","18359430_671072384","51062_1046501","43207049_1645982636","10206664_359325128","16327805_596571061","35862733_1342712619","23952779_951257173","1044491_29997907","37001209_1386188141","16609481_605504644","8293143_290749925","34612088_1303889179","46541430_1776527223","34934233_1308412560","5519153_191950698","24163448_921835113","22490783_820243142","37455682_1406170689","5663561_197087373","23228465_852257328","24977760_928375777","4584701_158096701","4554980_158174410","38190145_1438725541","42335103_1615259581","30494756_1148984974","871822_22044242","3629597_127330529","16276686_593522731","8956272_311874142","11584606_418073237","40547139_1541222371","15444366_562964888","8073474_284382416","12949451_475940773","1312896_39258805","2138508_73117603","30024829_1123999680","50190_1021069","19660211_719458755","11967859_439033608","12992293_480521962","16354271_596234812","23475959_913702154","33202112_1258773470","31916781_1195546198","30390418_1138120290","41159575_1566638844","5001055_208346382","11815307_427733906","1190299_34444356","29108187_1089031084","31585010_1182593594","21714655_922462715","40416210_1535898741","21825937_792001857","4473330_154106147","22767022_835411287","22681544_828885626","38922623_1471208367","1684825_56051090","48814347_1865817364","46466007_1779312168","34528527_1298814866","37082486_1389748213","27674410_1034532860","40888509_1561865756","42191270_1609800777","1070020_30682542","13304347_491821013","10962352_391767259","1721047_56371820","13821505_506533208","10943274_390988108","48755877_1863930410","35937919_1343345438","10091193_354525053","39121708_1489709623","41352252_1574214693","12454740_453864748","42921668_1635768371","3412031_137845152","12759538_468408265","40577240_1542420553","36636993_1370237641","41671513_1587895355","6777804_239460436","9498372_330390829","35657368_1658586609","21559783_781912388","37772035_1420382289","40389052_1534864882","28900452_1080965690","5181478_181904393","1662579_53913974","27919361_1043519438","44507651_1695364358","5002847_173150916","35647298_1332932098","11866530_429780260","40799541_1551826425","1878318_62625937","5771639_201045828","14828846_541243509","43772232_1679236679","5004158_177495161","43585036_1659796846","22391407_821021811","42252285_1750316359","40352475_1533125020","22787815_833407862","28885504_1144430075","39300475_1488037474","44637849_1707818141","44759203_1708069234","35642082_1332782672","21975267_798031604","228034_5348091","20534436_748165418","40956007_1559611293","46741139_1783903693","34525040_1294552347","23639523_870078703","20245045_742251831","15037924_548590114","3428288_120386125","39511066_1497290582","13644580_500082026","16504101_601855616","37617247_1419478023","20794910_756773732","6317091_221757433","39781326_1509095169","33302359_1260250935","25498279_949865055","10580767_375225260","9936530_348381631","7709878_272111119","9008149_313967678","2150859_73502769","39851655_1514757309","14900785_543464824","38596827_1456759408","40501615_1539263511","6390201_229901502","42246912_1611790175","26328364_983456346","40903335_1556198944","7745239_273132718","40282189_1535829169","37151525_1392746729","30276123_1133746683","23815247_892198660","1423982_67244612","22788624_833396891","37010668_1386568707","26942404_1007851114","40542978_1545853101","13369753_489548268","20960225_763526337","15994523_583005856","36776020_1376332628","7696770_271745949","36161892_1351771917","3220872_113430599","38704578_1461692788","25485788_955297387","24936374_942129999","11796249_441216204","36177913_1354133223","33591682_1261278997","19101433_698707472","15682832_575503033","44970163_1713821464","19216068_702709875","23854516_878661536","4793526_165687734","6206150_217466803","41389684_1576072299","31981308_1198249607","18806089_687770677","26160736_976928084","22406969_816557338","9985671_350396941","35336032_1322170355","36273940_1356177068","28539494_1066874590","1849136_61491564","49139325_1878123017","20536190_748237186","1884823_62861257","47702961_1821766880","17639771_643442115","11772040_425790935","39686525_1561704631","41879695_1596798701","28306024_1059025094","10390480_366972163","48345284_1849897422","39016801_1475531023","16034031_605418494","11615198_419284506","1741244_57080128","10246882_360999031","31625403_1184231957","40418556_1537808273","5897255_206915280","7837971_277314575","25089513_932533092","4784049_165406296","11083787_398828344","45357515_1729580639","9009074_314031072","18429375_673818324","44277849_1686420948","2146683_77644181","18479646_675539569","35332674_1322010559","1563916_49646095","5821791_203045947","18138322_662748851","1565882_49721418","41337732_1573740212","7125435_252846287","17222436_627909779","47229678_1803247128","47462737_1812280898","10122585_359935574","47828083_1827239668","17405290_634480320","48532746_1855272923","30212143_1131265600","30815377_1154142583","28344272_1059530945","267431_6177313","11649740_420652584","28217171_1054618597","23043408_844404355","23096072_881601576","28986950_1084368157","24601991_911540142","11407132_410625235","44905101_1718580880","8676791_313646765","9338392_324550666","14311931_523699900","22207138_807889618","22836533_835354152","11959559_433546629","40096085_1522415627","12175720_444979101","34969954_1309478506","38286863_1442678153","23473208_862567879","21022230_764084497","38622851_1457892593","4707453_175915663","49124396_1877589520","16953658_617985664","2831822_99630754","34812941_1304317829","49312308_1885621092","3519005_123630450","36154282_1355468183","47895606_1840135694","1545449_48897711","18873910_693741673","22036001_800619994","604529_13831332","2148272_73431878","38332785_1444876067","44796843_1731563673","3658363_128219477","23568492_872118367","28478983_1064951023","21474988_778866739","36900272_1384277855","14403870_526325009","19513269_713216254","22084411_802660651","48179344_1841305216","5295489_183615403","28471625_1064204080","4951685_171400707","28731670_1074474201","46619556_1779127064","40699340_1587812418","5380671_187433425","36742101_1380381221","28182334_1053269228","44470099_1719401832","39994037_1518262180","28049769_1048316939","40484897_1539228655","16251988_592646875","25082089_936928450","19796563_723699507","45073783_1717836308","35425738_1325190812","1197161_34698346","1079718_30051892","42592068_1624526221","32248408_1209260737","11698625_424855398","1721305_57413864","45672459_1742260779","40076439_1521627729","40584759_1543208877","22252824_809966777","12839621_469356263","20467366_759140781","44493313_1694668449","35071654_1314479085","22516905_821472079","29933664_1120547319","31036911_1162254206","10139534_356645253","11082820_396859915","33462985_1256437027","37086733_1389868364","28471320_1064250200","31458501_1178677941","959458_25455156","17564126_640460186","18383409_676258199","9600860_334273152","18275438_667885322","38525359_1453754271","11023819_394471458","11857249_429419795","10926053_390312658","9802109_342769250","42815652_1632221102","11646202_420541690","12381706_450933492","38241808_1443147292","34678744_1302180083","6571552_231364555","41872657_1596442876","15740242_593779962","35713826_1335438134","9484453_329824118","24475158_1278505796","28162556_1060507118","22688950_829274767","39252113_1486064147","2085440_71015699","8960064_314559106","49257277_1882412897","23338140_856992586","15033101_551445216","1078315_31597280","20013091_730860966","36094382_1349767240","41748692_1594475403","2810143_98799915","23961237_883426905","28411214_1061957041","15799576_577700223","45926614_1787636853","49083376_1876039825","38212667_1439865933","20003610_730573924","1969564_66243819","4909874_170245167","4238176_146089696","43238559_1647070934","9470313_338179118","20895721_760175493","24737950_937037702","37262328_1397548853","7167969_254086309","22607848_825587799","44487384_1694447611","26504786_991044310","14639691_534434533","13470184_505533833","13431444_491974436","49048959_1875042069","38901991_1470282424","38042025_1432118299","29671062_1110658112","26945699_1007979714","41254504_1570438656","37612146_1413046017","18391029_672456341","9771196_363019395","3147026_110706816","43784475_1667462391","11335444_407460135","6718606_237001259","37658800_1415146245","35500108_1327776688","40337124_1532607963","35190816_1316798625","14109345_516584701","35678784_1334099188","49326115_1884979220","21830930_792222381","17674399_658621718","27491977_1029050979","32033329_1328601378","43958406_1673837980","26982737_1009382720","23134891_848104671","37669101_1415547213","7876128_277488838","19849538_725590233","22882984_838139888","16778354_611940589","23622223_868973326","16127641_590104230","36642814_1370620254","41621489_1592732693","36828663_1378651395","2994253_105305024","33818392_1271178288","40695231_1581710921","29406723_1100463768","13652652_500396289","44260282_1698798267","12111291_440059435","12044022_437097842","12853719_470045039","15817525_576716923","7884539_280301655","4233909_145973453","11837715_444461252","28180136_1053176664","6651213_234429860","18914671_692909051","18148702_675343535","2123320_72432446","25861111_964930180","22141579_805044423","11196222_401854551","20267072_750914697","39168548_1482452714","13874115_508574273","30192908_1130647956","9757726_340888717","12496329_458221385","45631673_1740462861","20264139_739000824","1771938_58305193","29858364_1122085724","41472428_1582664169","4060674_146643474","22390540_815825605","37258395_1397462644","36175887_1352290618","22675029_828610673","21444548_777757920","23096072_862253744","33855646_1276980613","16843908_614133600","16056816_585442918","34000073_1276276594","19793285_723598488","33153081_1244981667","15149802_552519370","27495175_1028282788","35164665_1315889041","5062799_181825045","43561008_1658967181","23204703_851122356","40912575_1561730204","28386486_1061084922","36238268_1365227176","37009392_1386492004","37459079_1406281153","6266493_225337789","42067529_1604925874","1747807_59976697","3462841_121615234","19472758_711848494","39559404_1514537955","47362259_1808243186","49056569_1887684476","10529588_375160623","43409921_1653433458","37185550_1394151764","38514863_1532994723","432925_10357884","37606463_1412806455","36679428_1372545608","24178034_893218308","43376655_1679778212","35627414_1332236807","11343409_438294472","1539240_48626998","13933571_510665785","41749225_1608227466","26259433_983730029","48827514_1866347163","42843041_1633144368","47817621_1855102229","48568174_1856676911","17192340_626757845","2530485_88510785","44366352_1695697544","24362217_900774196","47590757_1817507553","33135117_1249104362","38282073_1442421466","38356042_1452902640","13053204_477728211","1452751_48102085","4853130_167893359","22350781_814305773","31315742_1172470780","24969792_931334426","3653238_128283864","44324640_1688180738","32039670_1201443678","13351027_488864947","14067332_515105539","22629584_828767659","11003213_420480441","48921217_1872210144","42580899_1629894147","22935244_839951722","29446948_1102113082","24992139_928462548","22749479_831714974","35066861_1312600808","35251532_1319025925","2268020_78199411","44751617_1704903501","26075993_974557900","41979723_1603784813","23194857_853577119","6189627_216817295","23737694_886714371","37066746_1388919478","47980212_1833138636","47037395_1798203228","42345849_1740729334","15570753_570998257","39788950_1540549269","25727127_959493528","29653764_1110002905","36287149_1356752263","27361408_1023556009","22535963_822410948","27645915_1033479847","23350877_857544928","1639278_52950687","4960435_174362415","24628631_915538259","17260739_629143176","30684431_1149131410","33882331_1280612970","10226778_360085637","35137134_1315003110","26645892_1004185367","4596122_158560736","849376_21209231","36006689_1347416843","29979921_1122374216","25787422_962091064","33472129_1256786335","8382910_336222845","13319594_487518761","31704347_1187436082","18462396_696730626","26957371_1008462019","19796228_723692334","5757937_200528044","13679251_507876139","36032672_1348245953","40679512_1546631339","9694345_338137844","24213007_894572088","48278304_1845423133","29131015_1089780045","24081707_888712087","1049113_28850165","48903092_1869193330","19926396_728201228","17808349_649860875","39521834_1497813673","29468654_1102791235","3971743_137509130","3387946_120306608","8368826_293197245","6873879_243125979","10383324_366677203","11572527_419968425","48483356_1853078429","6660346_234735495","4954550_171915839","32159727_1205690492","5903899_256049779","31939682_1196549894","49532186_1898868952","30215108_1131352520","15716344_572999679","29457134_1102604097","6264123_220017786","45365257_1729862784","23918385_881303570","42977669_1637658058","42321840_1614717930","17894203_652996529","49557424_1893537523","24101641_889642729","4498235_155018402","6390283_224565723","45666991_1742062671","38721157_1462172749","21808954_791417320","47369229_1808577719","33693151_1265001167","39739682_1511278712","8753583_305481633","36235090_1354548201","11272890_407444882","9448222_328450542","1320214_39504487","2788951_98144129","37686470_1416309291","24559538_909587432","23037773_844313528","48479800_1866437440","40246668_1528605784","13322261_487605288","49326558_1884989824","12928036_472964614","33227529_1247908199","298854_7029160","15852699_578669618","4745628_164082160","10598344_376069675","35907340_1349754212","2323123_80392100","4334791_149508606","7906592_280602663","40197020_1526500027","47558461_1816297617","49481353_1893766240","10528259_372886896","30121216_1127800590","33962879_1274961539","36147180_1355051144","34874831_1306430544","10582078_375283000","41566778_1583336977","44429011_1834438688","41148307_1566639884","3111653_111077159","45597473_1739126639","2890818_101617866","18727220_684918716","26994502_1009907728","2130638_72703580","43341057_1654389985","26156971_993603492","11933643_432588045","9593476_334134555","29499906_1115610778","35158744_1315707884","40057548_1520752327","23524850_864896080","14101365_516330180","41312243_1573018338","43312947_1652315916","13457948_492876494","41650551_1590633223","44263969_1691597626","48896652_1869027254","26686874_997632782","32376027_1214418269","45619075_1787225805","6233771_218741767","35187464_1317029177","41259382_1570688911","2523648_88164699","32391638_1215146297","10131136_369377030","22790126_838885469","18501135_676439914","21430110_777216280","48809094_1865794966","44859064_1709265472","43731184_1665500053","29483400_1105301765","10270379_361893425","30727797_1150719892","26365732_984913818","38366963_1452897381","34070170_1279452623","835052_20693935","18118994_662098345","36654996_1371116332","7703741_271908154","5530084_192353359","30744059_1151315962","18071997_660073101","33862663_1272156889","26082605_974000911","34717995_1301234731","1499064_46952642","33031513_1240443731","34985356_1310060132","27814362_1039538435","25096661_937779063","10475909_379313320","24215964_894660108","37175570_1397211040","10669352_389042402","46032435_1756370951","29784936_1114886832","1141996_37645162","23488180_863190831","16761896_625241393","30516571_1158708906","8366926_294402297","20307302_740334316","17344968_635953472","39135805_1481652617","44584781_1727663650","13838016_507196882","7127354_252579169","30399910_1145222315","7877637_277526806","10582122_377134782","30986492_1172080741","8552197_299049971","3957237_137951692","28122567_1095682856","35932400_1343158558","41929597_1598800982","26533613_991782023","23466781_862342541","11603961_418817369","18485331_680701652","15591782_568425062","29539858_1112262764","2988933_105119706","35546679_1329399427","15172778_600068993","25489430_949436245","1838974_63442638","5055369_174862164","11159527_400152451","9573552_333214658","48980685_1872151835","14636080_534321455","7819732_306289986","39573435_1499970795","11554977_416917692","49136603_1877981266","37636276_1414198456","15931849_580814593","39188916_1486802039","36724858_1374062297","39934679_1515824284","19848934_730701504","4152106_143342917","5458202_189666509","36934516_1383343203","11300006_406039829","14504630_530093364","4601642_158803965","5255394_182015760","14011604_513276700","48800531_1865282411","9649350_336230707","49353159_1885931520","3322265_152860600","43048704_1641732360","10192454_358812012","46654840_1780616256","26902022_1006306552","13981866_513841453","1795605_70406850","23449744_861476269","10763582_385601326","28242365_1055491597","41986757_1601198815","24213436_895764628","41225614_1569468114","17080768_625265320","47611925_1818541662","14734179_539245878","12863185_470300763","46617728_1781279798","23518228_864484920","39565139_1499662186","10396922_367205418","24668146_914351061","30831002_1174276506","47071332_1797069990","33708851_1265601784","10274663_362169921","44472232_1693924024","5860568_204472849","20285357_739629158","36545597_1366721763","31285444_1171484025","36452495_1364906780","28557463_1067575687","19304990_713025632","4953175_171456802","48497200_1853687319","26454180_990638075","12966869_474366789","46754576_1784505791","26806099_1002427423","32658995_1226058166","1172365_33791936","32884209_1234795165","35318834_1321537139","21433629_779748681","37396397_1403685243","16686145_613289811","1415535_50350394","45535446_1736795883","12710119_464108433","31851655_1193128623","46468264_1773253694","23509692_866473364","36567331_1367497624","26800361_1002189140","29057255_1086996002","22347761_822183475","36387065_1361955665","25354035_944245595","32096740_1204148656","36857207_1379880448","41177957_1567517257","44254145_1685631139","44349184_1712004568","5899672_205933835","13183853_482565766","478615_11111585","26774265_1037917254","13371438_489597244","17056600_622284838","26718049_998863301","37960964_1428377894","39196294_1483635379","11636239_420102910","13353275_488919878","37199309_1400534240","28125098_1051081906","46894102_1794637895","13465833_497376069","35810263_1339697843","32896176_1239447059","17530256_639101176","2148116_73403951","6447978_226497861","17432188_635750202","43319522_1658004245","48903980_1869505376","22604980_825497112","15981271_582570916","12388754_477087071","36982007_1385372980","25417139_946423544","23172745_849733703","1924610_66175897","12102647_439580766","578230_13404470","11637905_420141534","17590204_641526303","9059546_315154286","30833926_1159882607","2215078_76029115","6201184_241716643","15341809_559431666","37205435_1398004181","10155285_357225781","41262965_1583937703","19985852_730190098","28377783_1060733122","44274485_1686298202","42418660_1618263760","5970254_208568790","36475915_1363868099","36718594_1415591202","20678098_752773533","21429038_777228991","39109712_1490334982","24772461_918842430","22919021_839187289","46022763_1758737640","18725041_720960042","45881361_1750415636","8312710_291407377","6865140_242802927","38130594_1436202406","5477469_190354642","5578016_194077510","19636905_723334460","10150168_370937386","4152289_149016529","40743875_1551694955","2156075_76848758","16736729_638397335","29320076_1097310300","38240324_1442385537","26009224_970973477","2709840_95244518","23953282_882895548","649500_15832385","10903176_389268052","37480444_1407319562","28019691_1056446968","33324801_1251274859","27592464_1031552471","22598896_829256683","21983190_798380554","10619429_377087409","17831778_650679117","38665203_1463650717","3204643_112917834","26277664_994902766","31629456_1184402104","22891928_837967292","33558510_1260095378","44063330_1678014486","18818726_688296931","41176397_1567301442","38202963_1439193777","39281667_1487238009","30210847_1131282618","48613237_1858453188","45952605_1753018986","6934872_245404874","31315721_1172484980","15010436_547476441","23181426_850317772","45500284_1735184861","25791237_962231070","22768937_835140987","6116979_214067176","38507187_1452734231","33587641_1289861820","8695253_303660946","34494113_1297168329","33494436_1257634189","35670953_1333809400","5084978_175925684","46336623_1768174970","18146939_663034648","12186430_443008589","34336820_1288151064","37339421_1401067274","47944684_1852403157","16851266_616253475","25791350_969454993","21967727_797667705","23664824_870869499","22234818_838323391","25517596_1044227986","13118027_484876578","36736980_1374710891","11817408_427805193","41359557_1574501012","5272812_182744369","25345116_963549644","18266230_667525878","47784772_1826295130","18679155_683080652","45830733_1748600236","30183483_1130167230","10934103_390612214","11335179_412570422","40417958_1604507055","35286712_1323261151","36547782_1373079960","33014000_1239765195","48125411_1867356628","30746834_1159558838","26034487_972023247","30490714_1146936287","3295478_118674908","29469428_1103599657","39949604_1516399501","27238461_1019003903","38256164_1444008716","9772750_346099219","20867790_759274846","38324969_1444412063","22971897_841422749","29140415_1090193272","21373520_775332264","22723809_830547953","46913420_1790830537","25809755_963041010","1458189_45304424","31421882_1176964041","48577773_1856978474","13452670_503028326","10898105_388995275","25770319_969008505","47247307_1844764015","1287836_38243801","11153176_420338693","22798488_833846130","31333120_1179884622","39281149_1487273861","17892932_653029302","6638372_233898997","48823201_1866148878","13850241_507721571","35032611_1311452832","49076314_1875782221","4527987_156127222","36840693_1379140611","41658762_1587364524","43712062_1664582709","32162212_1210707520","47650463_1819970341","33120821_1243835453","38608068_1458894269","11439956_425353562","1447866_46645913","33069531_1241774797","17602955_642846284","3094943_108715623","47470303_1812968440","39471668_1495469230","4866654_168371872","20258239_740092953","917761_33653544","27149660_1015556926","18772981_689870557","24790150_919664308","23663587_870820668","1847614_61421955","36963135_1444944930","36101821_1350177554","19585108_715984180","13919646_510212617","10628963_377506220","24966607_927482774","18693817_683594653","20653851_754532016","35163604_1315846751","22777317_832955301","40969485_1558930078","20524212_747813583","12242698_445453843","36245340_1354999657","19369413_708265097","1862761_62085791","198004_4848477","14891628_546341353","17547844_639762566","38294959_1446201165","856051_21443571","28646403_1071259127","21669307_786089080","12968796_474494113","32886082_1238328066","31609834_1183551228","44209058_1688294008","46001866_1755271945","25796984_962593468","37483697_1407598581","1818745_60272910","16656929_607211069","39036050_1476470337","20743981_755090407","41782205_1611290125","9524404_333628946","28651942_1078477929","43584653_1664913001","10007658_351327563","40613216_1543834943","39240768_1637166162","36225153_1354611563","6564575_238813989","9851064_344652980","2019490_68543429","16377824_597127826","27042664_1019730814","35276007_1319819580","25053898_931091765","36234277_1354568647","37982534_1429472337","5131095_177666735","37108592_1390873078","43781504_1667298596","984011_26280371","18511882_677246989","44600058_1700525468","3314468_116729708","27941949_1051240798","39404753_1492519363","23194036_850670220","22646655_827362845","542209_12530517","32524331_1220511924","20030096_731377195","5090148_176135052","5343040_185868895","46005568_1755313932","31452839_1177719067","48300895_1846197169","28052873_1048704034","20712447_758942939","39198603_1487136272","48244814_1850916413","35781590_1337628951","3158902_111195627","39272920_1486848464","48452628_1868792720","26141786_976229891","40043748_1530342293","17537123_639349657","26630757_1003710225","2638325_92357477","21929219_796124388","24568842_910270308","8603388_300701822","9338215_324546126","15257863_558579991","6993370_251209488","31342299_1173624114","32216537_1207972301","40791308_1551584872","6012527_210119728","17672304_644614261","34043389_1277894765","3016860_116233607","27761095_1039136825","15383363_564284879","11359975_408476410","48989025_1872537798","11196424_401841236","25364161_944413919","18724632_695171388","23378428_868147320","10376502_367964459","12664975_462455020","8349454_292620407","12460872_460659915","37293259_1398907220","33064649_1241600449","28564393_1083258186","42026957_1605305132","21348622_774511259","9918088_347565822","11922954_433018555","47836272_1827016467","1182436_34136171","46825712_1787365045","44003460_1675588591","22231079_808926594","18091773_660835165","39682832_1506489328","2909950_102374371","11527870_416081663","35810639_1338686547","16541502_608773124","16389239_597532823","47270870_1804857024","1720243_56361113","40358952_1536714073","18636978_681548851","29666823_1110452097","47001926_1794217904","9591338_333900946","16273934_601945501","45773952_1749200231","40407623_1535522990","22232707_809015066","21614532_783940271","1316307_39404386","22776438_832812989","17843430_654279144","42172760_1609037561","12733712_467198255","26527752_991666230","30847922_1155325063","48600967_1858624310","31328633_1176227345","10987513_393077681","13614084_498933194","36516052_1365410163","22434377_817773228","38856979_1468520556","17689664_645263534","6704361_236465011","31975746_1212304147","39651071_1503459046","45197830_1722814238","38750327_1519954154","40568534_1542030942","12032049_436551199","22668382_828359849","40703958_1550011635","21691324_786969611","32816805_1241900042","42875805_1635219581","11172021_400656255","37176415_1393771073","36809313_1377997047","49217457_1882122052","8213122_288098466","25980510_969740177","37357110_1413258331","40669233_1546306709","44796394_1706699681","34665977_1299413130","37504870_1408333734","6018322_210424392","46797534_1786272031","40486424_1538639292","23865217_879540168","21920515_797844314","6355488_235899102","2195959_78094130","48245514_1844144858","7027222_249039736","18332211_670089796","31235318_1169613079","750202_19234046","30032070_1151887984","40403924_1549288711","41506168_1581007720","12635099_494971652","914513_29318632","35802442_1338388768","9783587_341986497","26887706_1005786893","48904676_1869268471","19136814_705283993","43705014_1664247587","49606320_1895307325","29891616_1126329706","47733798_1822938816","5089559_249457764","36121860_1350307928","41302188_1572331392","38400783_1451206545","12821050_468567780","24401397_902627186","23116392_847963747","24335441_899801099","4272563_154711814","17522255_714324224","31041574_1162479241","21351288_774595397","4585859_158115219","34236151_1284658676","28430053_1062706161","2214647_75967932","42036061_1603228846","36064171_1348040027","44114162_1680058840","10996783_397923347","10972921_392223033","5975315_208828902","21081590_765765729","30381450_1137827659","2467034_161582253","10990160_392942163","24434205_903914037","18179624_664311623","26712548_1008659270","43015759_1639063723","17280498_639969634","18658000_685362475","11327907_410807748","443244_10358161","7663942_270619402","43883912_1671833434","38468481_1451037340","16837455_620719325","6067577_216647386","17546494_639721112","49624411_1895899854","38389044_1447582579","13755481_504113182","42607605_1625063626","35215956_1317767456","32151385_1205341864","39822272_1510778499","18347042_670562855","42827177_1632568269","35280540_1328739183","48897311_1869109683","32938809_1239855989","18595127_682866902","12897073_471764266","24660318_914025028","26088836_976628556","11560252_417167174","14336038_535872876","36512404_1367437551","25681145_957426188","10491566_371348960","3027638_111896754","25720849_959195382","17639314_646430047","15453734_563452042","23367480_874037438","7570548_267867240","35146512_1315279791","24444839_904403849","24205954_894410919","5959192_212134268","21509869_780068551","10934610_390597620","32419048_1247787153","10533397_373070746","32431712_1216720355","14937040_544717876","13364741_489327515","38694781_1460919862","3904896_135386486","24976797_934059281","37157949_1393011448","39548403_1498868750","18523257_677212116","953003_42595981","32844984_1243472952","36215604_1353834214","13066570_478223836","11976074_434290596","33827663_1282123100","2747711_96557750","1040452_126196409","47318039_1807150239","16033910_584468599","43122864_1644942358","28873994_1079902712","26668255_1012089415","39133665_1480783576","23520963_864628884","27914158_1043204707","38026454_1434776425","32509253_1219827766","24571308_910313703","2463645_85904962","30166309_1143687668","359897_8795933","39567089_1502293558","16912523_654477621","31141168_1166009539","1406033_43761192","21417864_818139355","2192583_75221027","30988742_1229006980","39373998_1491067247","40282941_1530121410","2358226_81903007","6471959_227548939","6045280_211434817","12045756_437245210","4947760_171282610","9312269_323632108","40693599_1547219440","48141064_1845571405","40586820_1549530460","41980939_1609790938","15490684_564683501","2015891_68152446","10433544_368814928","4863383_168279448","43387695_1652615810","1588668_50709819","26697928_998000825","41824509_1594349839","26892962_1005992100","8785905_306520979","5385527_187065370","3454671_121370158","46211963_1763535376","24195381_900207122","20156480_735622839","11588063_418447366","13620613_499367929","9114062_316832330","240795_5783755","17913843_653750126","41288862_1575082409","10559621_374199963","45398662_1732523628","47317651_1812678820","43780110_1667434541","23063029_848760035","37366368_1402353368","40753800_1549750897","40769841_1550602009","34560875_1295698627","1243215_36581059","27411295_1025770978","1975315_66481818","27439741_1026405976","21459824_778277323","28715961_1080813430","11045936_395380201","23733495_873804769","23096438_1284949042","27154878_1024553026","28368038_1070206689","37357163_1410409489","24504771_1483685854","46410518_1772664641","8028399_283305358","46543623_1776096240","48189581_1868373889","25932779_978985427","6880330_244627484","4006190_155785840","5322650_184603154","17697774_645519502","15930883_580792582","11283495_405339773","37459126_1467609565","35744333_1336334278","17098480_625877675","49502941_1891588323","46918678_1790971359","2154919_73736049","20251149_738585382","24264513_964415881","3988828_144195586","26294078_982009002","12962917_502856169","24429606_922390529","36742456_1377435805","47406598_1810142439","1210772_35651988","11939953_436726196","9649103_336219104","41704556_1589488973","42618882_1627694979","37751826_1419416611","28373796_1062780980","36456674_1363113382","18903040_718635133","8896619_310041160","29702842_1111799133","4585207_158107413","11973086_434390048","31135616_1165806317","33277766_1249814933","26495191_990665688","38293274_1453360892","35206464_1317427610","49550116_1894292996","24256057_905182494","5078422_177372416","21693053_801878609","37393621_1403566783","24577841_910855120","9520717_331183509","8596627_300479410","39190749_1483326111","10363182_365790941","8379585_293511545","29604662_1108085406","43046085_1640305954","32384748_1216493862","43931504_1672881472","14267600_522051691","4039979_141788590","10123082_388756558","26750195_1043850082","47054971_1796378035","5727254_199379072","30687468_1149295820","26716665_1017054266","3585654_126625041","36375541_1360190495","1154461_33067850","40684807_1546883728","11808899_427464105","28352781_1059774388","2194895_75273231","36945320_1383705053","49329076_1885077731","19878420_726589148","10934329_390593285","39330555_1489358133","32355659_1219222880","39583750_1500590726","3065146_108108160","38680083_1467190401","44370529_1690073399","9270860_322077333","37123257_1391710489","7740438_278427951","6909592_244501619","32565917_1222245311","10575211_374947105","16840896_614053180","37818030_1422196797","11255738_404308569","262273_6059493","45864524_1749813458","9921199_347682717","47449099_1811728682","23259962_854361016","27699080_1041650909","4495604_154904113","46772796_1785203487","5116617_177290442","8307348_291198718","17776014_648556644","1198404_34929835","19073568_697728922","44536739_1731477550","2407404_83598714","2503767_87472017","7000406_247834195","26283469_990148794","22837266_835784160","7626851_286755480","13177333_482316518","41345516_1573932283","32485818_1223292149","26270261_981010432","9114363_316842346","35272742_1321534858","14348056_524657160","45539957_1780035765","36475614_1363895043","15458410_564056126","10971061_392146989","6572580_231414850","46639130_1779888464","25361788_946903107","34242250_1287746902","35074740_1313870070","36317254_1358001931","7127338_252582945","46066108_1757807210","1001411_26940182","17452970_636287724","2357213_86565844","21059174_765102321","9414242_328830311","19652516_718381137","23539581_865672743","30161468_1129331448","15412280_561746195","49104658_1877423469","19539176_714211105","12127363_459039778","12796072_467533245","22155794_805631210","4170474_143899469","1271397_37581947","40419915_1537353315","30664207_1153523994","28570542_1068102960","44320205_1688015467","23766190_875179886","23834543_877772025","43090061_1641821433","46369870_1769368949","2928606_103568868","44952531_1717100425","28501799_1067634933","24651931_913804036","16926175_617025471","41571354_1583578352","41495858_1580756820","6574672_231517860","35332779_1322013824","35188599_1316875736","21235974_770671016","24188504_899917806","39310697_1488574384","31550099_1181284884","34375952_1289275749","23248377_853198356","11464619_413165874","49277220_1884668680","35402090_1324869253","47117116_1798941277","11086635_410990877","6082050_213923877","36347287_1363618810","46233276_1764391716","6546845_230473061","25459893_954290251","30736076_1151066768","19702610_721745935","41823500_1594389039","5224493_180993438","23324483_856378254","18873597_690246374","434680_20276200","37075084_1389274441","40504211_1540753322","18754917_685831805","20119246_735416252","36927867_1383002573","5143110_178096116","12807119_467931555","46706902_1782558246","3351292_146772797","14466138_550225462","40885918_1608723845","13836183_508800339","36909801_1382135900","2072862_70553379","30428871_1139669928","39692927_1527272802","21366323_775072788","30046504_1124867884","24983090_928186262","32767435_1230459352","48484243_1860658887","5100346_179181205","2552325_90826692","27949268_1044471415","10361005_367850355","18698531_684060204","48759652_1863923075","42781591_1631167170","24500829_906879832","11692238_436540221","47479021_1812947317","35009187_1310719729","47899351_1829687527","43724685_1682416769","18774860_694028750","22389543_823584885","1531898_48258659","37762668_1419921872","45898079_1755365402","14838331_541511800","47512253_1815230835","40077066_1521572867","46306518_1769756661","15262542_556503120","43927961_1672734876","14463867_528389994","17705122_1212065398","21428940_778243873","40345723_1533373621","37935105_1427242723","13938264_516683459","11645872_421410725","2453554_85452107","24666018_917202326","27644801_1033372845","46124438_1761083297","31759452_1189660300","15255162_556204919","11257659_404354496","2771828_97623707","28203913_1054040555","14109956_516634480","47506065_1815607191","37817920_1427740702","27746871_1038215184","2968505_104386528","2219493_76131763","48809814_1865623586","30522732_1143318758","8336088_292207211","30473320_1240282344","20836632_758290232","47047746_1807001377","45552554_1737247361","32869319_1234300754","21694289_787033823","21660226_785708217","28561313_1067752044","4659283_164522769","35441265_1326281232","33064370_1242617058","48343531_1849349891","6216374_217837790","9224036_320523946","41253345_1570585340","11577141_418025961","20744843_755115731","9631032_335420105","40501199_1571312501","24340649_916632952","20176642_736264567","12681532_462949407","48256761_1844594780","32710530_1228193607","27079180_1015189485","20979858_762766583","26248199_983116343","35090728_1313385762","23419716_860267716","37473424_1406966542","21346086_774604202","49504225_1891613861","30067252_1125719939","17447765_636070626","42741417_1629868020","13674710_503354158","2679894_98220186","42023840_1619559052","46814429_1786892003","42067334_1604691115","1222879_38653297","7782103_274410341","605278_13840532","18943392_692797066","11386748_411792992","20223287_737624015","47386058_1811315430","17812997_650022486","15059706_550154323","30457744_1140908578","5737212_199882147","29182851_1091803169","43761156_1666523255","14185223_519226682","39717230_1506437023","32078369_1203273819","46250827_1764961061","15338226_582333533","11678269_421942129","31926373_1195938446","42011549_1602267431","14181048_520315960","10524428_372753990","24416973_903276287","27405095_1391122015","38794062_1485527973","10131740_356229539","2209620_77168413","32239461_1208972512","4905330_170186794","10989944_392960785","37682555_1416125528","76860_1606654","37605141_1412720840","22286356_811492725","23288108_854835430","11862399_429744062","35855986_1340355212","38752441_1463672725","16359712_599704317","1182619_34165637","33937347_1315220984","46547474_1776270801","24045846_893254066","22612452_825801061","16905891_616335446","44985343_1714320079","6224285_218169923","19537861_740879812","10982015_397392177","45722932_1744322034","46014898_1755580807","47294210_1832341601","17471660_636956981","11850835_429192917","7802363_275116565","47557350_1818044519","48596679_1859299364","45408804_1731561612","1932803_67372902","35945129_1343583793","46797688_1786478767","36448079_1362762563","41887099_1596966273","37339692_1401014368","12196136_443611264","43332992_1650603156","1804772_59758359","29179092_1091661617","1419387_43747057","29156676_1090915166","13867215_508503993","11676193_422010439","22847818_845968961","997388_26813371","16210274_590955785","8746840_305324580","27839940_1040378890","6123544_217158971","17698412_647916141","27197223_1017364329","1302061_38840637","26629234_995585025","17664143_775253076","48723302_1885067762","36937879_1383397496","25730114_959573140","6469489_242472299","36058485_1347830433","46810341_1786818787","42063799_1606075056","27583100_1031251985","36065866_1352754059","9832089_343907099","24447474_904463177","39911854_1525616380","45185871_1722408455","3046943_107028244","38499105_1457506334","10766144_383361614","36777570_1379094922","23794838_876223230","15341249_560626317","37962997_1428484737","24336791_899800360","20206359_737083580","12655233_461857139","31775149_1199062133","35499524_1364019786","15500613_565025672","35664623_1337629347","21975175_798005487","20867993_759325848","8437105_295291335","6126341_214360643","8863527_309654200","15482648_578046169","20699588_753516762","9971522_349815025","45678015_1760766628","44427751_1692044723","47146327_1800095220","17990179_657238911","27611077_1032246929","4932208_170848569","30797944_1153354750","48401944_1849879304","1404481_43046728","38193330_1438826852","36768804_1375991238","16906068_616334159","20364711_742284268","38019231_1437564645","22826101_834968625","4361073_150349890","9139549_318435580","15902656_579718242","17995089_657171912","38563774_1455316026","45037637_1716462832","29543888_1110201573","6556171_230802626","319191_7536329","1374026_41844475","40931947_1557469127","1381142_42120890","12974822_475454948","45877557_1750890282","15971298_585356349","11643046_434047472","4891568_171629419","46350569_1771595524","42613539_1625221709","12413234_455571705","32373755_1214340645","40039867_1520161836","9225302_320578383","1213867_44496601","34044455_1278719844","2217931_76068961","15708465_577854233","11561807_417232277","38473036_1451180536","36578282_1367807145","20343579_741613848","19514795_713266972",) + var filename: String = "" + val startState = JavaGrammar().getRsm() + + lateinit var fileContents: String + + @Setup(Level.Trial) + fun prepare() { + if (logger.handlers.isEmpty()) { + logger.addHandler(fh) + fh.setFormatter(SimpleFormatter()) + } + logger.info("Benchmarking Recovery GLL, Processing file number: ${filename}") + val gll = Gll(startState, + getTokenStream(File(pathToInput + filename).readText()), recovery = RecoveryMode.ON, reachability = ReachabilityMode.REACHABILITY) + val parseResult = gll.parse() + fileContents = buildStringFromSppf(parseResult.first!!).joinToString(" ") + } + + @Benchmark + @OutputTimeUnit(TimeUnit.NANOSECONDS) + fun measureGll(blackhole: Blackhole) { + val inputGraph = getTokenStream(fileContents) + val gll = Gll(startState, inputGraph, recovery = RecoveryMode.ON, reachability = ReachabilityMode.REACHABILITY) + + blackhole.consume(gll.parse()) + } +} diff --git a/benchmarks/src/jmh/kotlin/lexers/Java.x b/benchmarks/src/jmh/kotlin/lexers/Java.x deleted file mode 100644 index b54c7e5ba..000000000 --- a/benchmarks/src/jmh/kotlin/lexers/Java.x +++ /dev/null @@ -1,167 +0,0 @@ -package lexers; - -import java.io.*; - -%% - -%public -%class JavaLexer -%type JavaToken -%unicode - -Identifier = [:jletter:] [:jletterdigit:]* -IntegerLiteral = {DecimalIntegerLiteral} | {HexIntegerLiteral} | {OctalIntegerLiteral} | {BinaryIntegerLiteral} - -DecimalIntegerLiteral = {DecimalNumeral} [lL]? -HexIntegerLiteral = {HexNumeral} [lL]? -OctalIntegerLiteral = {OctalNumeral} [lL]? -BinaryIntegerLiteral = {BinaryNumeral} [lL]? - -DecimalNumeral = 0 | [1-9] {Digits}? | [1-9] "_"+ {Digits} -Digits = [0-9] | [0-9] (([0-9] | "_")+)? [0-9] - -HexNumeral = "0x" {HexDigits} | "0X" {HexDigits} -HexDigits = [0-9a-fA-F] ((([0-9a-fA-F] | "_")+)? [0-9a-fA-F])? - -OctalNumeral = 0 ("_"+)? {OctalDigits} -OctalDigits = [0-7] ((([0-7] | "_")+)? [0-7])? - -BinaryNumeral = 0 [bB] {BinaryDigits} -BinaryDigits = [0-1] ((([0-1] | "_")+)? [0-1])? - -FloatingPointLiteral = {DecimalFloatingPointLiteral} | {HexadecimalFloatingPointLiteral} -DecimalFloatingPointLiteral = [0-9] "." [0-9]? {ExponentPart}? [fFdD]? | "." [0-9] {ExponentPart}? [fFdD]? | [0-9] {ExponentPart} [fFdD] | [0-9] {ExponentPart}? [fFdD] -ExponentPart = [eE] [\+\-]? {Digits} -HexadecimalFloatingPointLiteral = {HexSignificand} {BinaryExponent} [fFdD]? -HexSignificand = {HexNumeral} "."? | 0 [xX] {HexDigits}? "." {HexDigits} -BinaryExponent = [pP] [\+\-]? {Digits} - -BooleanLiteral = "false" | "true" -NullLiteral = "null" - -InputCharacter = \\ "u"+ [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F] | [^\r\n\"\\] - -EscapeSequence = \\ [btnfr\"\'\\] | \\ ([0-7] | [0-7] [0-7] | [0-3][0-7][0-7]) -LineTerminator = \r | \n | \r\n - -CharacterLiteral = \' [^\r\n\'\\] \' | \' {EscapeSequence} \' - -StringLiteral = \" {StringCharacter}* \" -StringCharacter = {InputCharacter} | {EscapeSequence} -WhiteSpace = {LineTerminator} | [\ \t\f] - -Comment = {TraditionalComment} | {DocumentationComment} -TraditionalComment = "/*" [^*] ~"*/" | "/*" "*"+ "/" -DocumentationComment = "/**" {CommentContent} "*"+ "/" -CommentContent = ( [^*] | \*+ [^/*] )* - -%% - -"boolean" { return JavaToken.BOOLEAN; } -"byte" { return JavaToken.BYTE; } -"short" { return JavaToken.SHORT; } -"int" { return JavaToken.INT; } -"long" { return JavaToken.LONG; } -"char" { return JavaToken.CHAR; } -"float" { return JavaToken.FLOAT; } -"double" { return JavaToken.DOUBLE; } -"." { return JavaToken.DOT; } -"[" { return JavaToken.BRACKETLEFT; } -"]" { return JavaToken.BRACKETRIGHT; } -"(" { return JavaToken.PARENTHLEFT; } -")" { return JavaToken.PARENTHRIGHT; } -"{" { return JavaToken.CURLYLEFT; } -"}" { return JavaToken.CURLYRIGHT; } -"extends" { return JavaToken.EXTENDS; } -"&" { return JavaToken.ANDBIT; } -"<" { return JavaToken.LT; } -">" { return JavaToken.GT; } -";" { return JavaToken.SEMICOLON; } -":" { return JavaToken.COLON; } -"::" { return JavaToken.DOUBLECOLON; } -"..." { return JavaToken.ELLIPSIS; } -"," { return JavaToken.COMMA; } -"?" { return JavaToken.QUESTIONMARK; } -"super" { return JavaToken.SUPER; } -"package" { return JavaToken.PACKAGE; } -"import" { return JavaToken.IMPORT; } -"static" { return JavaToken.STATIC; } -"*" { return JavaToken.STAR; } -"+" { return JavaToken.PLUS; } -"-" { return JavaToken.MINUS; } -"%" { return JavaToken.PERCENT; } -"/" { return JavaToken.SLASH; } -"++" { return JavaToken.PLUSPLUS; } -"--" { return JavaToken.MINUSMINUS; } -"~" { return JavaToken.TILDA; } -"!" { return JavaToken.EXCLAMATIONMARK; } -"class" { return JavaToken.CLASS; } -"public" { return JavaToken.PUBLIC; } -"protected" { return JavaToken.PROTECTED; } -"private" { return JavaToken.PRIVATE; } -"abstract" { return JavaToken.ABSTRACT; } -"final" { return JavaToken.FINAL; } -"const" { return JavaToken.FINAL; } -"strictfp" { return JavaToken.STRICTFP; } -"implements" { return JavaToken.IMPLEMENTS; } -"transient" { return JavaToken.TRANSIENT; } -"volatile" { return JavaToken.VOLATILE; } -"=" { return JavaToken.ASSIGN; } -"*=" { return JavaToken.STARASSIGN; } -"/=" { return JavaToken.SLASHASSIGN; } -"+=" { return JavaToken.PLUSASSIGN; } -"-=" { return JavaToken.MINUSASSIGN; } -"%=" { return JavaToken.PERCENTASSIGN; } -"^=" { return JavaToken.XORASSIGN; } -"<<=" { return JavaToken.SHIFTLEFTASSIGN; } -">>=" { return JavaToken.SHIFTRIGHTASSIGN; } -">>>=" { return JavaToken.USRIGHTSHIFTASSIGN; } -"&=" { return JavaToken.ANDASSIGN; } -"|=" { return JavaToken.ORASSIGN; } -"||" { return JavaToken.OR; } -"&&" { return JavaToken.AND; } -"^" { return JavaToken.XORBIT; } -"==" { return JavaToken.EQ; } -"!=" { return JavaToken.NOTEQ; } -"<=" { return JavaToken.LESSEQ; } -">=" { return JavaToken.GREATEQ; } -"instanceof" { return JavaToken.INSTANCEOF; } -"synchronized" { return JavaToken.SYNCHRONIZED; } -"native" { return JavaToken.NATIVE; } -"void" { return JavaToken.VOID; } -"this" { return JavaToken.THIS; } -"throws" { return JavaToken.THROWS; } -"enum" { return JavaToken.ENUM; } -"interface" { return JavaToken.INTERFACE; } -"@" { return JavaToken.AT; } -"default" { return JavaToken.DEFAULT; } -"assert" { return JavaToken.ASSERT; } -"switch" { return JavaToken.SWITCH; } -"case" { return JavaToken.CASE; } -"while" { return JavaToken.WHILE; } -"for" { return JavaToken.FOR; } -"if" { return JavaToken.IF; } -"else" { return JavaToken.ELSE; } -"do" { return JavaToken.DO; } -"break" { return JavaToken.BREAK; } -"continue" { return JavaToken.CONTINUE; } -"return" { return JavaToken.RETURN; } -"throw" { return JavaToken.THROW; } -"try" { return JavaToken.TRY; } -"catch" { return JavaToken.CATCH; } -"finally" { return JavaToken.FINALLY; } -"|" { return JavaToken.ORBIT; } -"new" { return JavaToken.NEW; } -"->" { return JavaToken.ARROW; } - -{LineTerminator} {} -{Comment} {} -{WhiteSpace} {} -{CharacterLiteral} { return JavaToken.CHARLIT; } -{NullLiteral} { return JavaToken.NULLLIT; } -{StringLiteral} { return JavaToken.STRINGLIT; } -{FloatingPointLiteral} { return JavaToken.FLOATINGLIT; } -{BooleanLiteral} { return JavaToken.BOOLEANLIT; } -{IntegerLiteral} { return JavaToken.INTEGERLIT; } -{Identifier} { return JavaToken.ID; } -<> { return JavaToken.EOF; } \ No newline at end of file diff --git a/benchmarks/src/jmh/kotlin/lexers/JavaToken.kt b/benchmarks/src/jmh/kotlin/lexers/JavaToken.kt deleted file mode 100644 index 80d2dec46..000000000 --- a/benchmarks/src/jmh/kotlin/lexers/JavaToken.kt +++ /dev/null @@ -1,31 +0,0 @@ -package lexers - -import org.ucfs.parser.ParsingException -import org.ucfs.rsm.symbol.ITerminal - -enum class JavaToken : ITerminal { - ID, EOF, INTEGERLIT, FLOATINGLIT, BOOLEANLIT, CHARLIT, STRINGLIT, NULLLIT, - BOOLEAN, BYTE, SHORT, INT, LONG, CHAR, FLOAT, DOUBLE, DOT, BRACKETLEFT, BRACKETRIGHT, - PARENTHLEFT, PARENTHRIGHT, CURLYLEFT, CURLYRIGHT, EXTENDS, ANDBIT, LT, GT, - DIAMOND, SEMICOLON, COLON, DOUBLECOLON, ELLIPSIS, COMMA, QUESTIONMARK, SUPER, PACKAGE, - IMPORT, STATIC, STAR, PLUS, MINUS, PERCENT, SLASH, PLUSPLUS, MINUSMINUS, TILDA, EXCLAMATIONMARK, - CLASS, PUBLIC, PROTECTED, PRIVATE, FINAL, STRICTFP, IMPLEMENTS, TRANSIENT, VOLATILE, ASSIGN, - STARASSIGN, SLASHASSIGN, PLUSASSIGN, MINUSASSIGN, PERCENTASSIGN, XORASSIGN, SHIFTLEFTASSIGN, - SHIFTRIGHTASSIGN, USRIGHTSHIFTASSIGN, ANDASSIGN, ORASSIGN, OR, AND, XORBIT, EQ, NOTEQ, LESSEQ, - GREATEQ, INSTANCEOF, SYNCHRONIZED, NATIVE, VOID, THIS, THROWS, ENUM, INTERFACE, ABSTRACT, AT, DEFAULT, ASSERT, - SWITCH, CASE, WHILE, FOR, IF, ELSE, DO, BREAK, CONTINUE, RETURN, THROW, TRY, CATCH, FINALLY, ORBIT, NEW, ARROW; - - override fun getComparator(): Comparator { - return object : Comparator { - override fun compare(a: ITerminal, b: ITerminal): Int { - if (a !is JavaToken || b !is JavaToken) { - throw ParsingException( - "used comparator for $javaClass, " + - "but got elements of ${a.javaClass}$ and ${b.javaClass}\$" - ) - } - return a.ordinal - b.ordinal - } - } - } -} \ No newline at end of file diff --git a/benchmarks/src/jmh/kotlin/antlr4/Java8.g4 b/benchmarks/src/main/kotlin/org/antlr/Java8.g4 similarity index 99% rename from benchmarks/src/jmh/kotlin/antlr4/Java8.g4 rename to benchmarks/src/main/kotlin/org/antlr/Java8.g4 index 0a75e1741..af89ebed1 100644 --- a/benchmarks/src/jmh/kotlin/antlr4/Java8.g4 +++ b/benchmarks/src/main/kotlin/org/antlr/Java8.g4 @@ -55,7 +55,7 @@ Total lexer+parser time 30844ms. grammar Java8; @header { -package antlr4; +package org.antlr; } /* diff --git a/benchmarks/src/main/kotlin/org/iguana/Iguana.java b/benchmarks/src/main/kotlin/org/iguana/Iguana.java new file mode 100644 index 000000000..21f885dfc --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/Iguana.java @@ -0,0 +1,96 @@ +package org.iguana; + +import org.iguana.generator.ide.GenerateLangFiles; +import org.iguana.generator.ide.GenerateParserFiles; +import org.iguana.generator.ide.GeneratePsiElements; +import org.iguana.generator.parser.ParseTreeVisitorGenerator; +import org.iguana.generator.parser.ParserGenerator; +import org.iguana.grammar.Grammar; +import org.iguana.iggy.IggyParserUtils; +import org.iguana.util.serialization.JsonSerializer; +import picocli.CommandLine; +import picocli.CommandLine.ArgGroup; +import picocli.CommandLine.Command; +import picocli.CommandLine.Option; + +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.concurrent.Callable; + +@Command(name = "iguana", mixinStandardHelpOptions = true, version = "0.1-SNAPSHOT", + description = "Iguana: General Data-dependent Parser", sortOptions = false) +public class Iguana implements Callable { + + @ArgGroup(exclusive = true, multiplicity = "1") + Command command; + + static class Command { + @Option(names = "--generate-grammar") boolean generateGrammar; + @Option(names = "--generate-types") boolean generateTypes; + @Option(names = "--generate-ide") boolean generateIDE; + } + + @Option(names = {"--name", "-n"}, description = "The grammar name", defaultValue = "grammar") + private String grammarName; + + @Option(names = {"--grammar", "-g"}, description = "The grammar file", required = true) + private File grammarFile; + + @Option(names = {"--output", "-o"}, description = "The output project for generated files", required = true) + private Path outputDirectory; + + @Option(names = "--package", description = "package name for the generated code") + private String packageName; + + @Option(names = {"--grammar-output"}, description = "The location where the grammar.json file will be generated.", + required = true) + private Path grammarOutputDirectory; + + public static void main(String[] args) { + int exitCode = new CommandLine(new Iguana()).execute(args); + System.exit(exitCode); + } + + @Override + public Integer call() throws Exception { + // Create the gen directory if it doesn't already exist + Files.createDirectories(outputDirectory.toAbsolutePath()); + + Grammar grammar = IggyParserUtils.fromIggyGrammarPath(grammarFile.getAbsolutePath()); + + if (command.generateGrammar || command.generateTypes) { + String jsonPath = grammarOutputDirectory.resolve(grammarName + ".json").toAbsolutePath().toString(); + JsonSerializer.serialize(grammar, jsonPath); + System.out.println("grammar.json file has been generated in " + jsonPath); + Path typesOutputDirectory = outputDirectory.resolve(packageName.replace(".", "/")); + ParserGenerator parserGenerator = new ParserGenerator(grammarName, packageName, + typesOutputDirectory.toAbsolutePath().toString()); + parserGenerator.generateGrammar(); + } + + if (command.generateTypes) { + Path typesOutputDirectory = outputDirectory.resolve(packageName.replace(".", "/")); + ParserGenerator parserGenerator = new ParserGenerator(grammarName, packageName, + typesOutputDirectory.toAbsolutePath().toString()); + parserGenerator.generateParser(); + ParseTreeVisitorGenerator generator = new ParseTreeVisitorGenerator(grammar.toRuntimeGrammar(), grammarName, + packageName, typesOutputDirectory.toAbsolutePath().toString()); + generator.generate(); + } + + if (command.generateIDE) { + GenerateLangFiles generateLangFiles = new GenerateLangFiles(grammarName, + outputDirectory.toAbsolutePath().toString()); + generateLangFiles.generate(); + GeneratePsiElements generatePSIElements = new GeneratePsiElements(grammar.toRuntimeGrammar(), grammarName, + packageName, outputDirectory.toAbsolutePath().toString()); + generatePSIElements.generate(); + GenerateParserFiles generateParserFiles = new GenerateParserFiles(grammar.toRuntimeGrammar(), grammarName, + packageName, outputDirectory.toAbsolutePath().toString()); + generateParserFiles.generate(); + } + + return 0; + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/datadependent/ast/AST.java b/benchmarks/src/main/kotlin/org/iguana/datadependent/ast/AST.java new file mode 100644 index 000000000..8e2dc0809 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/datadependent/ast/AST.java @@ -0,0 +1,985 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.datadependent.ast; + +import org.iguana.datadependent.env.IEvaluatorContext; +import org.iguana.datadependent.env.intarray.MutableLong; +import org.iguana.datadependent.values.Stack; +import org.iguana.grammar.exception.AssertionFailedException; +import org.iguana.grammar.exception.UnexpectedTypeOfArgumentException; +import org.iguana.sppf.NonPackedNode; +import org.iguana.utils.input.Input; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; + +import static org.iguana.utils.string.StringUtil.listToString; + +public class AST { + + public static final Object UNDEF = new Object() { + public String toString() { + return "UNDEF"; + } + }; + + /** + * Expressions + */ + public static final Expression TRUE = Expression.Boolean.TRUE; + public static final Expression FALSE = Expression.Boolean.FALSE; + + public static Expression.Integer integer(java.lang.Integer value) { + return new Expression.Integer(value); + } + + public static Expression real(java.lang.Float value) { + return new Expression.Real(value); + } + + public static Expression string(java.lang.String value) { + return new Expression.String(value); + } + + public static Expression.Not not(Expression exp) { + return new Expression.Not(exp); + } + + public static Expression tuple(Expression... args) { + return new Expression.Tuple(args); + } + + public static Expression intTuple2(Expression.Integer element1, Expression.Integer element2) { + return new Expression.IntTuple2(element1, element2); + } + + public static Expression.Name var(java.lang.String name) { + return new Expression.Name(name); + } + + public static Expression var(java.lang.String name, int i) { + return new Expression.Name(name, i); + } + + public static class Println extends Expression.Call { + Println(Expression... arguments) { + super("println", arguments); + } + + @Override + public Object interpret(IEvaluatorContext ctx, Input input) { + List arguments = interpretArguments(ctx, input); + for (Object argument : arguments) { + System.out.print(argument); + System.out.print("; "); + } + System.out.println(); + return null; + } + } + + public static Println println(Expression... args) { + return new Println(args); + } + + public static class Assert extends Expression.Call { + + Assert(Expression... arguments) { + super("assert", arguments); + } + + @Override + public Object interpret(IEvaluatorContext ctx, Input input) { + for (Expression argument : arguments) { + Object value = argument.interpret(ctx, input); + if (!(value instanceof java.lang.Boolean)) { + throw new UnexpectedTypeOfArgumentException(this); + } + if (!((java.lang.Boolean) value)) { + throw new AssertionFailedException(argument); + } + } + return null; + } + } + + public static Assert assertion(Expression... args) { + return new Assert(args); + } + + public static class Set extends Expression.Call { + + Set(Expression... arguments) { + super("set", arguments); + } + + @Override + public Object interpret(IEvaluatorContext ctx, Input input) { + List objects = interpretArguments(ctx, input); + return new HashSet<>(objects); + } + } + + public static Set set(Expression... args) { + return new Set(args); + } + + public static class Indent extends Expression.Call { + + Indent(Expression arg) { + super("indent", arg); + } + + @Override + public Object interpret(IEvaluatorContext ctx, Input input) { + Expression arg = arguments[0]; + Object value = arg.interpret(ctx, input); + if (!(value instanceof java.lang.Integer)) { + throw new UnexpectedTypeOfArgumentException(this); + } + + return input.getColumnNumber((java.lang.Integer) value); + } + } + + public static Indent indent(Expression... args) { + if (args.length != 1) throw new RuntimeException("args size should be one"); + return indent(args[0]); + } + + public static Indent indent(Expression arg) { + return new Indent(arg); + } + + public static class PPDeclare extends Expression.Call { + public PPDeclare(Expression variable, Expression value) { + super("ppDeclare", variable, value); + } + + @Override + public Object interpret(IEvaluatorContext ctx, Input input) { + Expression variable = arguments[0]; + Expression value = arguments[1]; + + Object var = variable.interpret(ctx, input); + + if (!(var instanceof NonPackedNode)) + throw new UnexpectedTypeOfArgumentException(this); + + NonPackedNode node = (NonPackedNode) var; + + ctx.declareGlobalVariable(input.subString(node.getLeftExtent(), node.getRightExtent()), + value.interpret(ctx, input)); + + return null; + } + } + + public static PPDeclare ppDeclare(Expression variable, Expression value) { + return new PPDeclare(variable, value); + } + + public static class PPLookup extends Expression.Call { + + PPLookup(Expression... arguments) { + super("ppLookup", arguments); + } + + @Override + public Object interpret(IEvaluatorContext ctx, Input input) { + Object value = arguments[0].interpret(ctx, input); + if (!(value instanceof NonPackedNode)) { + throw new UnexpectedTypeOfArgumentException(this); + } + + NonPackedNode node = (NonPackedNode) value; + + java.lang.String subString = input.subString(node.getLeftExtent(), node.getRightExtent()); + + if (subString.equals("true")) + return true; + else if (subString.equals("false")) + return false; + + Object obj = ctx.lookupGlobalVariable(subString); + return obj != null ? obj : false; + } + } + + public static PPLookup ppLookup(Expression arg) { + return new PPLookup(arg); + } + + public static class EndsWith extends Expression.Call { + + EndsWith(Expression... arguments) { + super("endsWith", arguments); + } + + @Override + public Object interpret(IEvaluatorContext ctx, Input input) { + Expression index = arguments[0]; + Expression character = arguments[1]; + + Object i = index.interpret(ctx, input); + if (!(i instanceof java.lang.Integer)) { + throw new UnexpectedTypeOfArgumentException(this); + } + + int j = (java.lang.Integer) i; + + Object c = character.interpret(ctx, input); + + if (!(c instanceof java.lang.String)) { + throw new UnexpectedTypeOfArgumentException(this); + } + + Object obj = input.subString(j - 1, j); + return obj.equals(c); + } + + @Override + public java.lang.String toString() { + return java.lang.String.format("endsWith(%s,\"%s\")", arguments[0], arguments[1]); + } + + } + + public static EndsWith endsWith(Expression index, Expression character) { + return new EndsWith(index, character); + } + + public static class StartsWith extends Expression.Call { + + StartsWith(Expression... arguments) { + super("startsWith", arguments); + } + + @Override + public Object interpret(IEvaluatorContext ctx, Input input) { + Object i = arguments[0].interpret(ctx, input); + if (!(i instanceof java.lang.Integer)) { + throw new UnexpectedTypeOfArgumentException(this); + } + + int j = (java.lang.Integer) i; + + for (int k = 1; k < arguments.length; k++) { + Object str = arguments[k].interpret(ctx, input); + + if (!(str instanceof java.lang.String)) { + throw new UnexpectedTypeOfArgumentException(this); + } + + int len = j + ((java.lang.String) str).length(); + if (len < input.length()) { + Object obj = input.subString(j, len); + if (obj.equals(str)) + return true; + } + } + return false; + } + } + + public static StartsWith startsWith(Expression... args) { + return new StartsWith(args); + } + + public static class Neg extends Expression.Call { + Neg(Expression... arguments) { + super("neg", arguments); + } + + @Override + public Object interpret(IEvaluatorContext ctx, Input input) { + Object value = arguments[0].interpret(ctx, input); + if (!(value instanceof java.lang.Integer)) { + throw new UnexpectedTypeOfArgumentException(this); + } + int v = (java.lang.Integer) value; + return -v; + } + + @Override + public java.lang.String toString() { + return java.lang.String.format("-(%s)", arguments[0]); + } + } + + public static Neg neg(Expression arg) { + return new Neg(arg); + } + + public static class Len extends Expression.Call { + Len(Expression... arguments) { + super("len", arguments); + } + + @Override + public Object interpret(IEvaluatorContext ctx, Input input) { + Object value = arguments[0].interpret(ctx, input); + if (!(value instanceof NonPackedNode)) { + throw new UnexpectedTypeOfArgumentException(this); + } + + NonPackedNode node = (NonPackedNode) value; + + return node.getRightExtent() - node.getLeftExtent(); + } + } + + public static Len len(Expression arg) { + return new Len(arg); + } + + public static class Pr1 extends Expression.Call { + Pr1(Expression... arguments) { + super("pr1", arguments); + } + + @Override + public Object interpret(IEvaluatorContext ctx, Input input) { + Expression arg1 = arguments[0]; + Expression arg2 = arguments[0]; + Expression arg3 = arguments[0]; + int v = (java.lang.Integer) arg1.interpret(ctx, input); + int curr = (java.lang.Integer) arg2.interpret(ctx, input); + + if (v >= curr) + return v; + + int prev = (java.lang.Integer) arg3.interpret(ctx, input); // prev is actually previous plus one + + if (v >= prev) + return curr; + + return 0; + } + } + + public static Pr1 pr1(Expression arg1, Expression arg2, Expression arg3) { + return new Pr1(arg1, arg2, arg3); + } + + public static class Pr2 extends Expression.Call { + private final Expression arg1; + private final Expression arg2; + private final Expression[] arg3; + + Pr2(Expression arg1, Expression arg2, Expression[] arg3) { + super("pr2", getArgs(arg1, arg2, arg3)); + this.arg1 = arg1; + this.arg2 = arg2; + this.arg3 = arg3; + } + + private static Expression[] getArgs(Expression arg1, Expression arg2, Expression[] arg3) { + Expression[] args = new Expression[arg3.length + 2]; + args[0] = arg1; + args[1] = arg2; + int i = 2; + for (Expression arg : arg3) + args[i++] = arg; + + return args; + } + + @Override + public Object interpret(IEvaluatorContext ctx, Input input) { + int v = (java.lang.Integer) arg1.interpret(ctx, input); + int curr = (java.lang.Integer) arg2.interpret(ctx, input); + + if (v >= curr) + return v; + + int prev = (java.lang.Integer) arg3[0].interpret(ctx, input); + + if (v >= prev) + return curr; + + for (int i = 1; i < arg3.length; i++) { + prev = (java.lang.Integer) arg3[i].interpret(ctx, input); + + if (v >= prev) + return prev; + } + + return 0; + } + + @Override + public java.lang.String toString() { + return java.lang.String.format("pr1(%s,%s,%s)", arg1, arg2, listToString(arg3, ",")); + } + } + + public static Pr2 pr2(Expression arg1, Expression arg2, Expression[] arg3) { + return new Pr2(arg1, arg2, arg3); + } + + public static class Pr3 extends Expression.Call { + Pr3(Expression... arguments) { + super("pr3", arguments); + } + + @Override + public Object interpret(IEvaluatorContext ctx, Input input) { + + int v1 = (java.lang.Integer) arguments[0].interpret(ctx, input); + int v2 = (java.lang.Integer) arguments[1].interpret(ctx, input); + + if (v1 == 0) + return v2; + + if (v2 == 0) + return v1; + + return java.lang.Integer.min(v1, v2); + } + } + + public static Pr3 pr3(Expression arg1, Expression arg2) { + return new Pr3(arg1, arg2); + } + + public static class Min extends Expression.Call { + Min(Expression... arguments) { + super("min", arguments); + } + + @Override + public Object interpret(IEvaluatorContext ctx, Input input) { + int v1 = (java.lang.Integer) arguments[0].interpret(ctx, input); + int v2 = (java.lang.Integer) arguments[1].interpret(ctx, input); + return java.lang.Integer.min(v1, v2); + } + } + + public static Min min(Expression arg1, Expression arg2) { + return new Min(arg1, arg2); + } + + public static class Map extends Expression.Call { + Map(Expression... arguments) { + super("map", arguments); + } + + @Override + public Object interpret(IEvaluatorContext ctx, Input input) { + return new HashMap<>(); + } + } + + public static Map map() { + return new Map(); + } + + public static class Put extends Expression.Call { + + Put(Expression... arguments) { + super("put", arguments); + } + + @Override + public Object interpret(IEvaluatorContext ctx, Input input) { + Object value = arguments[0].interpret(ctx, input); + if (!(value instanceof java.util.Set)) + throw new UnexpectedTypeOfArgumentException(this); + + @SuppressWarnings("unchecked") + java.util.Set s = (java.util.Set) value; + + value = arguments[1].interpret(ctx, input); + if (!s.contains(value)) { + s = new HashSet<>(s); + s.add(value); + } + + return s; + } + } + + public static Put put(Expression... args) { + if (args.length != 2) throw new RuntimeException("args size should be two"); + return put(args[0], args[1]); + } + + public static Put put(Expression arg1, Expression arg2) { + return new Put(arg1, arg2); + } + + public static class Put3 extends Expression.Call { + Put3(Expression... arguments) { + super("put", arguments); + } + + @Override + public Object interpret(IEvaluatorContext ctx, Input input) { + Object value = arguments[0].interpret(ctx, input); + if (!(value instanceof java.util.Map)) + throw new UnexpectedTypeOfArgumentException(this); + + @SuppressWarnings("unchecked") + java.util.Map m = (java.util.Map) value; + + m = new HashMap<>(m); + m.put(arguments[1].interpret(ctx, input), arguments[2].interpret(ctx, input)); + + return m; + } + } + + public static Put3 put(Expression arg1, Expression arg2, Expression arg3) { + return new Put3(arg1, arg2, arg3); + } + + public static class Contains extends Expression.Call { + + Contains(Expression... arguments) { + super("contains", arguments); + } + + @Override + public Object interpret(IEvaluatorContext ctx, Input input) { + Object value = arguments[0].interpret(ctx, input); + if (!(value instanceof java.util.Set)) + throw new UnexpectedTypeOfArgumentException(this); + + @SuppressWarnings("unchecked") + java.util.Set s = (java.util.Set) value; + + value = arguments[1].interpret(ctx, input); + + return s.contains(value); + } + } + + public static Contains contains(Expression... args) { + if (args.length != 2) throw new RuntimeException("args size should be two"); + return contains(args[0], args[1]); + } + + public static Contains contains(Expression arg1, Expression arg2) { + return new Contains(arg1, arg2); + } + + public static class Push extends Expression.Call { + + Push(Expression... arguments) { + super("push", arguments); + } + + @Override + public Object interpret(IEvaluatorContext ctx, Input input) { + Expression arg1 = arguments[0]; + Expression arg2 = arguments[1]; + Object value = arg1.interpret(ctx, input); + + if (value == null) + return Stack.from(arg2.interpret(ctx, input)); + + if (!(value instanceof Stack)) + throw new UnexpectedTypeOfArgumentException(this); + + @SuppressWarnings("unchecked") + Stack s = (Stack) value; + + return s.push(arg2.interpret(ctx, input)); + } + + @Override + public java.lang.String toString() { + return java.lang.String.format("push(%s,%s)", arguments[0], arguments[1]); + } + } + + public static Push push(Expression arg1, Expression arg2) { + return new Push(arg1, arg2); + } + + public static class Pop extends Expression.Call { + Pop(Expression... arguments) { + super("pop", arguments); + } + + @Override + public Object interpret(IEvaluatorContext ctx, Input input) { + Object value = arguments[0].interpret(ctx, input); + if (!(value instanceof Stack)) + throw new UnexpectedTypeOfArgumentException(this); + + @SuppressWarnings("unchecked") + Stack s = (Stack) value; + + return s.pop(); + } + + @Override + public java.lang.String toString() { + return java.lang.String.format("pop(%s)", arguments[0]); + } + } + + public static Pop pop(Expression arg) { + return new Pop(arg); + } + + public static class Top extends Expression.Call { + Top(Expression... arguments) { + super("top", arguments); + } + + @Override + public Object interpret(IEvaluatorContext ctx, Input input) { + Object value = arguments[0].interpret(ctx, input); + if (!(value instanceof Stack)) + throw new UnexpectedTypeOfArgumentException(this); + + @SuppressWarnings("unchecked") + Stack s = (Stack) value; + + return s.top(); + } + + @Override + public java.lang.String toString() { + return java.lang.String.format("top(%s)", arguments[0]); + } + } + + public static Top top(Expression arg) { + return new Top(arg); + } + + public static class Find extends Expression.Call { + Find(Expression... arguments) { + super("find", arguments); + } + + @Override + public Object interpret(IEvaluatorContext ctx, Input input) { + Object value = arguments[0].interpret(ctx, input); + if (!(value instanceof Stack)) + throw new UnexpectedTypeOfArgumentException(this); + + @SuppressWarnings("unchecked") + Stack> s = + (Stack>) value; + + java.lang.String key = (java.lang.String) arguments[0].interpret(ctx, input); + + java.lang.Boolean hit; + while (s != null) { + hit = s.top().get(key); + if (hit != null) + return hit; + s = s.pop(); + } + + return false; + } + } + + public static Find find(Expression arg1, Expression arg2) { + return new Find(arg1, arg2); + } + + public static class Get extends Expression.Call { + private final Expression arg1; + private final int arg2; + + Get(Expression arg1, int arg2) { + super("get", arg1, AST.integer(arg2)); + this.arg1 = arg1; + this.arg2 = arg2; + } + + @Override + public Object interpret(IEvaluatorContext ctx, Input input) { + Object result = arg1.interpret(ctx, input); + if (result instanceof MutableLong) { + MutableLong value = (MutableLong) result; + if (arg2 == 0) { + return value.getHigherOrderInt(); + } + return value.getLowerOrderInt(); + } + if (result instanceof Long) { + long value = (Long) result; + if (arg2 == 0) { + return (int) (value >> 32); + } + return (int) (value & 0xffffffffL); + } + Object[] value = (Object[]) result; + return value[arg2]; + } + + @Override + public java.lang.String toString() { + return java.lang.String.format("%s.%s", arg1, arg2); + } + } + + public static Get get(Expression arg1, int arg2) { + return new Get(arg1, arg2); + } + + public static class Get2 extends Expression.Call { + + Get2(Expression... arguments) { + super("get", arguments); + } + + @Override + public Object interpret(IEvaluatorContext ctx, Input input) { + Expression arg1 = arguments[0]; + Expression arg2 = arguments[1]; + Object result = arg1.interpret(ctx, input); + if (result instanceof MutableLong) { + MutableLong value = (MutableLong) result; + int i = (java.lang.Integer) arg2.interpret(ctx, input); + if (i == 0) { + return value.getHigherOrderInt(); + } + return value.getLowerOrderInt(); + } + if (result instanceof Long) { + long value = (Long) result; + int i = (java.lang.Integer) arg2.interpret(ctx, input); + if (i == 0) { + return (int) (value >> 32); + } + return (int) (value & 0xffffffffL); + } + Object[] value = (Object[]) result; + int i = (java.lang.Integer) arg2.interpret(ctx, input); + return value[i]; + } + + @Override + public java.lang.String toString() { + return java.lang.String.format("%s.%s", arguments[0], arguments[1]); + } + } + + public static Get2 get(Expression arg1, Expression arg2) { + return new Get2(arg1, arg2); + } + + public static class Shift extends Expression.Call { + + Shift(Expression... arguments) { + super("shift", arguments); + } + + @Override + public Object interpret(IEvaluatorContext ctx, Input input) { + int i = (java.lang.Integer) arguments[0].interpret(ctx, input); + if (i == 0) + return 0; + + int j = (java.lang.Integer) arguments[1].interpret(ctx, input); + return i & j; + } + + @Override + public java.lang.String toString() { + return java.lang.String.format("%s<<%s", arguments[0], arguments[1]); + } + } + + public static Shift shift(Expression arg1, Expression arg2) { + return new Shift(arg1, arg2); + } + + public static class Undef extends Expression.Call { + Undef(Expression... arguments) { + super("undef", arguments); + } + + @Override + public Object interpret(IEvaluatorContext ctx, Input input) { + return UNDEF; + } + + @Override + public java.lang.String toString() { + return UNDEF.toString(); + } + } + + public static Undef undef() { + return new Undef(); + } + + public static Expression lShiftANDEqZero(Expression lhs, Expression rhs) { + return new Expression.LShiftANDEqZero(lhs, rhs); + } + + public static Expression orIndent(Expression index, Expression ind, Expression first, Expression lExt) { + return new Expression.OrIndent(index, ind, first, lExt); + } + + public static Expression andIndent(Expression index, Expression first, Expression lExt) { + return new Expression.AndIndent(index, first, lExt); + } + + public static Expression andIndent(Expression index, Expression first, Expression lExt, boolean returnIndex) { + return new Expression.AndIndent(index, first, lExt, returnIndex); + } + + public static Expression.Or or(Expression lhs, Expression rhs) { + return new Expression.Or(lhs, rhs); + } + + public static Expression.And and(Expression lhs, Expression rhs) { + return new Expression.And(lhs, rhs); + } + + public static Expression.Less less(Expression lhs, Expression rhs) { + return new Expression.Less(lhs, rhs); + } + + public static Expression.LessThanEqual lessEq(Expression lhs, Expression rhs) { + return new Expression.LessThanEqual(lhs, rhs); + } + + public static Expression.Greater greater(Expression lhs, Expression rhs) { + return new Expression.Greater(lhs, rhs); + } + + public static Expression.GreaterThanEqual greaterEq(Expression lhs, Expression rhs) { + return new Expression.GreaterThanEqual(lhs, rhs); + } + + public static Expression.Equal equal(Expression lhs, Expression rhs) { + return new Expression.Equal(lhs, rhs); + } + + public static Expression.NotEqual notEqual(Expression lhs, Expression rhs) { + return new Expression.NotEqual(lhs, rhs); + } + + public static Expression.LeftExtent lExt(String label) { + return new Expression.LeftExtent(label); + } + + public static Expression.RightExtent rExt(String label) { + return new Expression.RightExtent(label); + } + + public static Expression.Yield yield(String label) { + return new Expression.Yield(label); + } + + public static Expression.Yield yield(String label, int i) { + return new Expression.Yield(label, i); + } + + public static Expression.Val val(String label) { + return new Expression.Val(label); + } + + public static Expression.EndOfFile endOfFile(Expression index) { + return new Expression.EndOfFile(index); + } + + public static Expression ifThenElse(Expression condition, Expression thenPart, Expression elsePart) { + return new Expression.IfThenElse(condition, thenPart, elsePart); + } + + public static Expression.Assignment assign(java.lang.String id, Expression exp) { + return new Expression.Assignment(id, exp); + } + + public static Expression.Assignment assign(java.lang.String id, int i, Expression exp) { + return new Expression.Assignment(id, i, exp); + } + + // + // Statements + // + public static Statement stat(Expression exp) { + return new Statement.Expression(exp); + } + + public static Statement varDeclStat(String name) { + return new Statement.VariableDeclaration(new VariableDeclaration(name)); + } + + public static Statement varDeclStat(String name, Expression exp) { + return new Statement.VariableDeclaration(new VariableDeclaration(name, exp)); + } + + public static Statement varDeclStat(String name, int i) { + return new Statement.VariableDeclaration(new VariableDeclaration(name, i)); + } + + public static Statement varDeclStat(String name, int i, Expression exp) { + return new Statement.VariableDeclaration(new VariableDeclaration(name, i, exp)); + } + + public static Statement varDeclStat(VariableDeclaration varDecl) { + return new Statement.VariableDeclaration(varDecl); + } + + public static VariableDeclaration varDecl(String name) { + return new VariableDeclaration(name); + } + + public static VariableDeclaration varDecl(String name, Expression exp) { + return new VariableDeclaration(name, exp); + } + + public static VariableDeclaration varDecl(String name, int i) { + return new VariableDeclaration(name, i); + } + + public static VariableDeclaration varDecl(String name, int i, Expression exp) { + return new VariableDeclaration(name, i, exp); + } + + public static Expression.Add add(Expression lhs, Expression rhs) { + return new Expression.Add(lhs, rhs); + } + + public static Expression.Subtract subtract(Expression lhs, Expression rhs) { + return new Expression.Subtract(lhs, rhs); + } + + public static Expression.Multiply multiply(Expression lhs, Expression rhs) { + return new Expression.Multiply(lhs, rhs); + } + + public static Expression.Divide divide(Expression lhs, Expression rhs) { + return new Expression.Divide(lhs, rhs); + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/datadependent/ast/AbstractAST.java b/benchmarks/src/main/kotlin/org/iguana/datadependent/ast/AbstractAST.java new file mode 100644 index 000000000..ebc0c2fba --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/datadependent/ast/AbstractAST.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.datadependent.ast; + +import org.iguana.datadependent.attrs.AbstractAttrs; +import org.iguana.datadependent.env.IEvaluatorContext; +import org.iguana.datadependent.traversal.IAbstractASTVisitor; +import org.iguana.utils.input.Input; + +public abstract class AbstractAST extends AbstractAttrs { + + public abstract Object interpret(IEvaluatorContext ctx, Input input); + + public abstract T accept(IAbstractASTVisitor visitor); + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/datadependent/ast/Expression.java b/benchmarks/src/main/kotlin/org/iguana/datadependent/ast/Expression.java new file mode 100644 index 000000000..d0a8e5e29 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/datadependent/ast/Expression.java @@ -0,0 +1,1474 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.datadependent.ast; + +import org.iguana.datadependent.env.IEvaluatorContext; +import org.iguana.datadependent.traversal.IAbstractASTVisitor; +import org.iguana.grammar.exception.UndeclaredVariableException; +import org.iguana.grammar.exception.UnexpectedTypeOfArgumentException; +import org.iguana.result.RecognizerResult; +import org.iguana.sppf.NonPackedNode; +import org.iguana.sppf.NonterminalNodeWithValue; +import org.iguana.utils.input.Input; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +import static java.lang.Integer.toUnsignedLong; +import static org.iguana.utils.string.StringUtil.listToString; + +public abstract class Expression extends AbstractAST { + + public boolean isBoolean() { + return false; + } + + public abstract static class Boolean extends Expression { + + static final Boolean TRUE = new Boolean() { + + @Override + public Object interpret(IEvaluatorContext ctx, Input input) { + return true; + } + + @Override + public java.lang.String toString() { + return "true"; + } + + @Override + public T accept(IAbstractASTVisitor visitor) { + return visitor.visit(this); + } + }; + + static final Boolean FALSE = new Boolean() { + + @Override + public Object interpret(IEvaluatorContext ctx, Input input) { + return false; + } + + @Override + public java.lang.String toString() { + return "false"; + } + + @Override + public T accept(IAbstractASTVisitor visitor) { + return visitor.visit(this); + } + + }; + + public boolean isBoolean() { + return true; + } + } + + public boolean isInteger() { + return false; + } + + public static class Integer extends Expression { + + private final java.lang.Integer value; + + Integer(java.lang.Integer value) { + this.value = value; + } + + public boolean isInteger() { + return true; + } + + public int getValue() { + return value; + } + + @Override + public Object interpret(IEvaluatorContext ctx, Input input) { + return value; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (!(obj instanceof Integer)) return false; + Integer other = (Integer) obj; + return this.value.equals(other.value); + } + + @Override + public int hashCode() { + return value.hashCode(); + } + + @Override + public java.lang.String toString() { + return java.lang.String.valueOf(value); + } + + @Override + public T accept(IAbstractASTVisitor visitor) { + return visitor.visit(this); + } + + } + + public static class Real extends Expression { + + private final java.lang.Float value; + + Real(java.lang.Float value) { + this.value = value; + } + + @Override + public Object interpret(IEvaluatorContext ctx, Input input) { + return value; + } + + @Override + public java.lang.String toString() { + return value.toString(); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (!(obj instanceof Real)) return false; + Real other = (Real) obj; + return this.value.equals(other.value); + } + + @Override + public int hashCode() { + return value.hashCode(); + } + + @Override + public T accept(IAbstractASTVisitor visitor) { + return visitor.visit(this); + } + + } + + public boolean isString() { + return false; + } + + public static class String extends Expression { + + private final java.lang.String value; + + String(java.lang.String value) { + this.value = value; + } + + public boolean isString() { + return true; + } + + @Override + public Object interpret(IEvaluatorContext ctx, Input input) { + return value; + } + + @Override + public java.lang.String toString() { + return value; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (!(obj instanceof String)) return false; + String other = (String) obj; + return this.value.equals(other.value); + } + + @Override + public int hashCode() { + return value.hashCode(); + } + + @Override + public T accept(IAbstractASTVisitor visitor) { + return visitor.visit(this); + } + } + + public static class Not extends Expression { + + private final Expression exp; + + Not(Expression exp) { + this.exp = exp; + } + + public Expression getExp() { + return exp; + } + + @Override + public Object interpret(IEvaluatorContext ctx, Input input) { + Object value = exp.interpret(ctx, input); + if (!(value instanceof java.lang.Boolean)) { + throw new UnexpectedTypeOfArgumentException(this); + } + return !(java.lang.Boolean) value; + } + + @Override + public boolean equals(Object obj) { + if (obj == this) return true; + if (!(obj instanceof Not)) return false; + Not other = (Not) obj; + return other.exp == this.exp; + } + + @Override + public int hashCode() { + return exp.hashCode(); + } + + @Override + public T accept(IAbstractASTVisitor visitor) { + return visitor.visit(this); + } + + @Override + public java.lang.String toString() { + return "!" + exp; + } + } + + public static class Tuple extends Expression { + + private final Expression[] elements; + private final int length; + + Tuple(Expression... elements) { + this.elements = elements; + for (Expression element : elements) + if (element == null) + throw new RuntimeException("Expressions of a tuple should not be null."); + this.length = elements.length; + } + + public int length() { + return length; + } + + public Expression[] getElements() { + return elements; + } + + @Override + public Object interpret(IEvaluatorContext ctx, Input input) { + if (length == 1) + return elements[0].interpret(ctx, input); + + Object[] values = new Object[elements.length]; + for (int i = 0; i < elements.length; i++) { + values[i] = elements[i].interpret(ctx, input); + } + + return values; + } + + @Override + public java.lang.String toString() { + return "(" + listToString(Arrays.stream(elements).map(Object::toString) + .collect(Collectors.toList()), ",") + ")"; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (!(obj instanceof Tuple)) return false; + Tuple other = (Tuple) obj; + return this.length == other.length && Arrays.equals(this.elements, other.elements); + } + + @Override + public int hashCode() { + return Objects.hash(Arrays.hashCode(this.elements), this.length); + } + + @Override + public T accept(IAbstractASTVisitor visitor) { + return visitor.visit(this); + } + } + + public static class IntTuple2 extends Expression { + + private final Integer element1; + private final Integer element2; + private final Long longValue; + + IntTuple2(Integer element1, Integer element2) { + this.element1 = element1; + this.element2 = element2; + this.longValue = toUnsignedLong(element1.value) << 32 | toUnsignedLong(element2.value); + } + + public Integer getElement1() { + return element1; + } + + public Integer getElement2() { + return element2; + } + + @Override + public Object interpret(IEvaluatorContext ctx, Input input) { + return longValue; + } + + @Override + public java.lang.String toString() { + return "(" + element1 + ", " + element2 + ")"; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (!(obj instanceof IntTuple2)) return false; + IntTuple2 other = (IntTuple2) obj; + return element1.equals(other.element1) && element2.equals(other.element2); + } + + @Override + public int hashCode() { + return Objects.hash(element1, element2); + } + + @Override + public T accept(IAbstractASTVisitor visitor) { + return visitor.visit(this); + } + } + + public static class Name extends Expression { + + private final java.lang.String name; + private final int i; + + Name(java.lang.String name) { + this(name, -1); + } + + Name(java.lang.String name, int i) { + this.name = name; + this.i = i; + } + + public java.lang.String getName() { + return name; + } + + @Override + public Object interpret(IEvaluatorContext ctx, Input input) { + Object value = i != -1 ? ctx.lookupVariable(i) : ctx.lookupVariable(name); + if (value == null) { + throw new UndeclaredVariableException(name); + } + return value; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (!(obj instanceof Name)) return false; + Name other = (Name) obj; + return this.name.equals(other.name) && this.i == other.i; + } + + @Override + public int hashCode() { + return Objects.hash(this.name, this.i); + } + + @Override + public java.lang.String toString() { + return name + (i != -1 ? ":" + i : ""); + } + + @Override + public T accept(IAbstractASTVisitor visitor) { + return visitor.visit(this); + } + + } + + public abstract static class Call extends Expression { + + protected final java.lang.String fun; + protected final Expression[] arguments; + + Call(java.lang.String fun, Expression... arguments) { + this.fun = fun; + this.arguments = arguments == null ? new Expression[0] : arguments; + } + + public java.lang.String getFunName() { + return fun; + } + + public Expression[] getArguments() { + return this.arguments; + } + + protected List interpretArguments(IEvaluatorContext ctx, Input input) { + List objects = new ArrayList<>(); + for (Expression argument : arguments) { + objects.add(argument.interpret(ctx, input)); + } + return objects; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (!(obj instanceof Call)) return false; + Call other = (Call) obj; + return this.fun.equals(other.fun) && Arrays.equals(this.arguments, other.arguments); + } + + @Override + public int hashCode() { + return Objects.hash(this.fun, Arrays.hashCode(this.arguments)); + } + + @Override + public java.lang.String toString() { + return java.lang.String.format("%s(%s)", fun, listToString(arguments, ",")); + } + + @Override + public T accept(IAbstractASTVisitor visitor) { + return visitor.visit(this); + } + + } + + public static class Assignment extends Expression { + + private final java.lang.String id; + private final int i; + private final Expression exp; + + Assignment(java.lang.String id, Expression exp) { + this(id, -1, exp); + } + + Assignment(java.lang.String id, int i, Expression exp) { + this.id = id; + this.i = i; + this.exp = exp; + } + + public java.lang.String getId() { + return id; + } + + public Expression getExpression() { + return exp; + } + + @Override + public Object interpret(IEvaluatorContext ctx, Input input) { + if (i != -1) + ctx.storeVariable(i, exp.interpret(ctx, input)); + else + ctx.storeVariable(id, exp.interpret(ctx, input)); + return null; + } + + @Override + public java.lang.String toString() { + return i != -1 ? java.lang.String.format("%s:%s = %s", id, i, exp) + : java.lang.String.format("%s = %s", id, exp); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (!(obj instanceof Assignment)) return false; + Assignment other = (Assignment) obj; + return this.id.equals(other.id) && this.i == other.i && this.exp.equals(other.exp); + } + + @Override + public int hashCode() { + return Objects.hash(this.id, this.i, this.exp); + } + + @Override + public T accept(IAbstractASTVisitor visitor) { + return visitor.visit(this); + } + + } + + public static class LShiftANDEqZero extends Expression { + + private final Expression lhs; + private final Expression rhs; + + LShiftANDEqZero(Expression lhs, Expression rhs) { + this.lhs = lhs; + this.rhs = rhs; + } + + public Expression getLhs() { + return lhs; + } + + public Expression getRhs() { + return rhs; + } + + @Override + public Object interpret(IEvaluatorContext ctx, Input input) { + Object lhs = this.lhs.interpret(ctx, input); + Object rhs = this.rhs.interpret(ctx, input); + + if (lhs instanceof java.lang.Integer && rhs instanceof java.lang.Integer) + return (((java.lang.Integer) lhs) & (1 << ((java.lang.Integer) rhs))) == 0; + + throw new UnexpectedTypeOfArgumentException(this); + } + + @Override + public java.lang.String toString() { + return java.lang.String.format("%s&(1<<%s) == 0", lhs, rhs); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (!(obj instanceof LShiftANDEqZero)) return false; + LShiftANDEqZero other = (LShiftANDEqZero) obj; + return this.lhs.equals(other.lhs) && this.rhs.equals(other.rhs); + } + + @Override + public int hashCode() { + return Objects.hash(this.lhs, this.rhs); + } + + + @Override + public T accept(IAbstractASTVisitor visitor) { + return visitor.visit(this); + } + + } + + public static class OrIndent extends Expression { + + private final Expression index; + private final Expression ind; + private final Expression first; + private final Expression lExt; + + OrIndent(Expression index, Expression ind, Expression first, Expression lExt) { + this.index = index; + this.ind = ind; + this.first = first; + this.lExt = lExt; + } + + public Expression getIndex() { + return index; + } + + public Expression getIndent() { + return ind; + } + + public Expression getFirst() { + return first; + } + + public Expression getLExt() { + return lExt; + } + + @Override + public Object interpret(IEvaluatorContext ctx, Input input) { + + int ind = (java.lang.Integer) this.ind.interpret(ctx, input); + + if (ind == 0) + return true; + + int first = (java.lang.Integer) this.first.interpret(ctx, input); + int lExt; + if (first == 1) { + + int index = (java.lang.Integer) this.index.interpret(ctx, input); + lExt = (java.lang.Integer) this.lExt.interpret(ctx, input); + + if (lExt - index == 0) + return true; + else { + int indent = input.getColumnNumber(lExt); + return indent > ind; + } + + } else { + lExt = (java.lang.Integer) this.lExt.interpret(ctx, input); + int indent = input.getColumnNumber(lExt); + return indent > ind; + } + + } + + @Override + public java.lang.String toString() { + // return ind + " == 0 || (" + first + " && " + lExt + " - " + index + " == 0) || + // indent(" + lExt + ") > " + ind; + return java.lang.String.format("f(%s,%s,%s,%s)", index, ind, first, lExt); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (!(obj instanceof OrIndent)) return false; + OrIndent other = (OrIndent) obj; + return this.index.equals(other.index) + && this.ind.equals(other.ind) + && this.first.equals(other.first) + && this.lExt.equals(other.lExt); + } + + @Override + public int hashCode() { + return Objects.hash(this.index, this.ind, this.first, this.lExt); + } + + + @Override + public T accept(IAbstractASTVisitor visitor) { + return visitor.visit(this); + } + + } + + public static class AndIndent extends Expression { + + private final Expression index; + private final Expression first; + private final Expression lExt; + + private final boolean returnIndex; + + AndIndent(Expression index, Expression first, Expression lExt) { + this.index = index; + this.first = first; + this.lExt = lExt; + this.returnIndex = false; + } + + AndIndent(Expression index, Expression first, Expression lExt, boolean returnIndex) { + this.index = index; + this.first = first; + this.lExt = lExt; + this.returnIndex = returnIndex; + } + + public Expression getIndex() { + return index; + } + + public Expression getFirst() { + return first; + } + + public Expression getLExt() { + return lExt; + } + + @Override + public Object interpret(IEvaluatorContext ctx, Input input) { + int first = (java.lang.Integer) this.first.interpret(ctx, input); + if (first == 1) { + + int index = (java.lang.Integer) this.index.interpret(ctx, input); + int lExt = (java.lang.Integer) this.lExt.interpret(ctx, input); + + if (lExt - index == 0) + return returnIndex ? index : 1; + } + + return 0; + } + + @Override + public java.lang.String toString() { +// return returnIndex? "(" +first + " && " + lExt + " - " + index + " == 0)?" + index +// : first + " && " + lExt + " - " + index + " == 0"; + return returnIndex ? java.lang.String.format("g(%s,%s,%s,%s)", index, first, lExt, 1) + : java.lang.String.format("g(%s,%s,%s,%s)", index, first, lExt, 0); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (!(obj instanceof AndIndent)) return false; + AndIndent other = (AndIndent) obj; + return this.index.equals(other.index) + && this.first.equals(other.first) + && this.lExt.equals(other.lExt); + } + + @Override + public int hashCode() { + return Objects.hash(this.index, this.first, this.lExt); + } + + + @Override + public T accept(IAbstractASTVisitor visitor) { + return visitor.visit(this); + } + + } + + public abstract static class BinaryExpression extends Expression { + protected final Expression lhs; + protected final Expression rhs; + private final java.lang.String symbolName; + + protected BinaryExpression(Expression lhs, Expression rhs, java.lang.String symbolName) { + this.lhs = lhs; + this.rhs = rhs; + this.symbolName = symbolName; + } + + public Expression getLhs() { + return lhs; + } + + public Expression getRhs() { + return rhs; + } + + @Override + public java.lang.String toString() { + return java.lang.String.format("%s %s %s", lhs, symbolName, rhs); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (!(obj instanceof BinaryExpression)) return false; + BinaryExpression other = (BinaryExpression) obj; + return this.lhs.equals(other.lhs) + && this.rhs.equals(other.rhs) + && Objects.equals(this.symbolName, other.symbolName); + } + + @Override + public int hashCode() { + return Objects.hash(this.lhs, this.rhs, this.symbolName); + } + } + + public static class Or extends BinaryExpression { + + Or(Expression lhs, Expression rhs) { + super(lhs, rhs, "||"); + } + + @Override + public Object interpret(IEvaluatorContext ctx, Input input) { + boolean lhs = (java.lang.Boolean) this.lhs.interpret(ctx, input); + if (lhs) return true; + + return this.rhs.interpret(ctx, input); + } + + @Override + public T accept(IAbstractASTVisitor visitor) { + return visitor.visit(this); + } + } + + public static class And extends BinaryExpression { + + And(Expression lhs, Expression rhs) { + super(lhs, rhs, "&&"); + } + + @Override + public Object interpret(IEvaluatorContext ctx, Input input) { + boolean lhs = (java.lang.Boolean) this.lhs.interpret(ctx, input); + if (!lhs) return false; + + return this.rhs.interpret(ctx, input); + } + + @Override + public T accept(IAbstractASTVisitor visitor) { + return visitor.visit(this); + } + + } + + public static class Less extends BinaryExpression { + + Less(Expression lhs, Expression rhs) { + super(lhs, rhs, "<"); + } + + @Override + public Object interpret(IEvaluatorContext ctx, Input input) { + Object lhs = this.lhs.interpret(ctx, input); + Object rhs = this.rhs.interpret(ctx, input); + + if (lhs instanceof java.lang.Integer && rhs instanceof java.lang.Integer) { + return ((java.lang.Integer) lhs) < ((java.lang.Integer) rhs); + } + + if (lhs instanceof java.lang.Float && rhs instanceof java.lang.Float) { + return ((java.lang.Float) lhs) < ((java.lang.Float) rhs); + } + + throw new UnexpectedTypeOfArgumentException(this); + } + + @Override + public T accept(IAbstractASTVisitor visitor) { + return visitor.visit(this); + } + + } + + public static class LessThanEqual extends BinaryExpression { + + LessThanEqual(Expression lhs, Expression rhs) { + super(lhs, rhs, "<="); + } + + @Override + public Object interpret(IEvaluatorContext ctx, Input input) { + Object lhs = this.lhs.interpret(ctx, input); + Object rhs = this.rhs.interpret(ctx, input); + + if (lhs instanceof java.lang.Integer && rhs instanceof java.lang.Integer) { + return ((java.lang.Integer) lhs) <= ((java.lang.Integer) rhs); + } + + if (lhs instanceof java.lang.Float && rhs instanceof java.lang.Float) { + return ((java.lang.Float) lhs) <= ((java.lang.Float) rhs); + } + + throw new UnexpectedTypeOfArgumentException(this); + } + + @Override + public T accept(IAbstractASTVisitor visitor) { + return visitor.visit(this); + } + + } + + public static class Greater extends BinaryExpression { + + Greater(Expression lhs, Expression rhs) { + super(lhs, rhs, ">"); + } + + @Override + public Object interpret(IEvaluatorContext ctx, Input input) { + Object lhs = this.lhs.interpret(ctx, input); + Object rhs = this.rhs.interpret(ctx, input); + + if (lhs instanceof java.lang.Integer && rhs instanceof java.lang.Integer) { + return ((java.lang.Integer) lhs) > ((java.lang.Integer) rhs); + } + + if (lhs instanceof java.lang.Float && rhs instanceof java.lang.Float) { + return ((java.lang.Float) lhs) > ((java.lang.Float) rhs); + } + + throw new UnexpectedTypeOfArgumentException(this); + } + + @Override + public T accept(IAbstractASTVisitor visitor) { + return visitor.visit(this); + } + + } + + public static class GreaterThanEqual extends BinaryExpression { + + GreaterThanEqual(Expression lhs, Expression rhs) { + super(lhs, rhs, ">="); + } + + public Expression getLhs() { + return lhs; + } + + public Expression getRhs() { + return rhs; + } + + @Override + public Object interpret(IEvaluatorContext ctx, Input input) { + Object lhs = this.lhs.interpret(ctx, input); + Object rhs = this.rhs.interpret(ctx, input); + + if (lhs instanceof java.lang.Integer && rhs instanceof java.lang.Integer) { + return ((java.lang.Integer) lhs) >= ((java.lang.Integer) rhs); + } + + if (lhs instanceof java.lang.Float && rhs instanceof java.lang.Float) { + return ((java.lang.Float) lhs) >= ((java.lang.Float) rhs); + } + + throw new UnexpectedTypeOfArgumentException(this); + } + + @Override + public T accept(IAbstractASTVisitor visitor) { + return visitor.visit(this); + } + + } + + public static class Equal extends BinaryExpression { + + Equal(Expression lhs, Expression rhs) { + super(lhs, rhs, "=="); + } + + @Override + public Object interpret(IEvaluatorContext ctx, Input input) { + Object lhs = this.lhs.interpret(ctx, input); + Object rhs = this.rhs.interpret(ctx, input); + + if (lhs == AST.UNDEF || rhs == AST.UNDEF) { + return lhs == rhs; + } + + if (lhs instanceof java.lang.Integer && rhs instanceof java.lang.Integer) { + return lhs.equals(rhs); + } + + if (lhs instanceof java.lang.Float && rhs instanceof java.lang.Float) { + return lhs.equals(rhs); + } + + if (lhs instanceof java.lang.String && rhs instanceof java.lang.String) { + return lhs.equals(rhs); + } + + throw new UnexpectedTypeOfArgumentException(this); + } + + @Override + public T accept(IAbstractASTVisitor visitor) { + return visitor.visit(this); + } + + } + + public static class NotEqual extends BinaryExpression { + + NotEqual(Expression lhs, Expression rhs) { + super(lhs, rhs, "!="); + } + + @Override + public Object interpret(IEvaluatorContext ctx, Input input) { + Object lhs = this.lhs.interpret(ctx, input); + Object rhs = this.rhs.interpret(ctx, input); + + if (lhs instanceof java.lang.Integer && rhs instanceof java.lang.Integer) { + return lhs != rhs; + } + + if (lhs instanceof java.lang.Float && rhs instanceof java.lang.Float) { + return lhs != rhs; + } + + throw new UnexpectedTypeOfArgumentException(this); + } + + @Override + public T accept(IAbstractASTVisitor visitor) { + return visitor.visit(this); + } + + } + + public static class Add extends BinaryExpression { + + public Add(Expression lhs, Expression rhs) { + super(lhs, rhs, "+"); + } + + @Override + public Object interpret(IEvaluatorContext ctx, Input input) { + Object lhs = this.lhs.interpret(ctx, input); + Object rhs = this.rhs.interpret(ctx, input); + + if (lhs instanceof java.lang.Integer && rhs instanceof java.lang.Integer) + return (java.lang.Integer) lhs + (java.lang.Integer) rhs; + + if (lhs instanceof java.lang.Float && rhs instanceof java.lang.Float) + return (java.lang.Float) lhs + (java.lang.Float) rhs; + + throw new UnexpectedTypeOfArgumentException(this); + } + + @Override + public T accept(IAbstractASTVisitor visitor) { + return visitor.visit(this); + } + } + + public static class Subtract extends BinaryExpression { + + public Subtract(Expression lhs, Expression rhs) { + super(lhs, rhs, "-"); + } + + @Override + public Object interpret(IEvaluatorContext ctx, Input input) { + Object lhs = this.lhs.interpret(ctx, input); + Object rhs = this.rhs.interpret(ctx, input); + + if (lhs instanceof java.lang.Integer && rhs instanceof java.lang.Integer) + return (java.lang.Integer) lhs - (java.lang.Integer) rhs; + + if (lhs instanceof java.lang.Float && rhs instanceof java.lang.Float) + return (java.lang.Float) lhs - (java.lang.Float) rhs; + + throw new UnexpectedTypeOfArgumentException(this); + } + + @Override + public T accept(IAbstractASTVisitor visitor) { + return visitor.visit(this); + } + } + + public static class Multiply extends BinaryExpression { + + public Multiply(Expression lhs, Expression rhs) { + super(lhs, rhs, "*"); + } + + @Override + public Object interpret(IEvaluatorContext ctx, Input input) { + Object lhs = this.lhs.interpret(ctx, input); + Object rhs = this.rhs.interpret(ctx, input); + + if (lhs instanceof java.lang.Integer && rhs instanceof java.lang.Integer) + return (java.lang.Integer) lhs * (java.lang.Integer) rhs; + + if (lhs instanceof java.lang.Float && rhs instanceof java.lang.Float) + return (java.lang.Float) lhs * (java.lang.Float) rhs; + + throw new UnexpectedTypeOfArgumentException(this); + } + + @Override + public T accept(IAbstractASTVisitor visitor) { + return visitor.visit(this); + } + } + + public static class Divide extends BinaryExpression { + + public Divide(Expression lhs, Expression rhs) { + super(lhs, rhs, "/"); + } + + @Override + public Object interpret(IEvaluatorContext ctx, Input input) { + Object lhs = this.lhs.interpret(ctx, input); + Object rhs = this.rhs.interpret(ctx, input); + + if (lhs instanceof java.lang.Integer && rhs instanceof java.lang.Integer) + return (java.lang.Integer) lhs / (java.lang.Integer) rhs; + + if (lhs instanceof java.lang.Float && rhs instanceof java.lang.Float) + return (java.lang.Float) lhs / (java.lang.Float) rhs; + + throw new UnexpectedTypeOfArgumentException(this); + } + + @Override + public T accept(IAbstractASTVisitor visitor) { + return visitor.visit(this); + } + } + + public static class LeftExtent extends Expression { + + public static java.lang.String format = "%s.lExt"; + + private final java.lang.String label; + + LeftExtent(java.lang.String label) { + this.label = label; + } + + public java.lang.String getLabel() { + return label; + } + + @Override + public Object interpret(IEvaluatorContext ctx, Input input) { + Object value = ctx.lookupVariable(java.lang.String.format(format, label)); + if (value == null) { + throw new UndeclaredVariableException(label + "." + "lExt"); + } + return value; + } + + @Override + public java.lang.String toString() { + return java.lang.String.format("%s.lExt", label); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (!(obj instanceof LeftExtent)) return false; + LeftExtent other = (LeftExtent) obj; + return this.label.equals(other.label); + } + + @Override + public int hashCode() { + return this.label.hashCode(); + } + + @Override + public T accept(IAbstractASTVisitor visitor) { + return visitor.visit(this); + } + + } + + public static class RightExtent extends Expression { + + public static java.lang.String format = "%s.rExt"; + + private final java.lang.String label; + + RightExtent(java.lang.String label) { + this.label = label; + } + + public java.lang.String getLabel() { + return label; + } + + @Override + public Object interpret(IEvaluatorContext ctx, Input input) { + Object value = ctx.lookupVariable(label); + if (value == null) { + throw new UndeclaredVariableException(label); + } + + if (value instanceof NonPackedNode) { + return ((NonPackedNode) value).getRightExtent(); + // In case of recognizer, we don't have a node. The right extent will be equal to the recognized value. + } else if (value instanceof RecognizerResult) { + return ((RecognizerResult) value).getRightExtent(); + } else { + throw new UnexpectedTypeOfArgumentException(this); + } + } + + @Override + public java.lang.String toString() { + return java.lang.String.format("%s.rExt", label); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (!(obj instanceof RightExtent)) return false; + RightExtent other = (RightExtent) obj; + return this.label.equals(other.label); + } + + @Override + public int hashCode() { + return this.label.hashCode(); + } + + @Override + public T accept(IAbstractASTVisitor visitor) { + return visitor.visit(this); + } + + } + + public static class Yield extends Expression { + + public static java.lang.String format = "%s.yield"; + + private final java.lang.String label; + private final int i; + + Yield(java.lang.String label) { + this(label, -1); + } + + Yield(java.lang.String label, int i) { + this.label = label; + this.i = i; + } + + public java.lang.String getLabel() { + return label; + } + + @Override + public Object interpret(IEvaluatorContext ctx, Input input) { + Object value = i == -1 ? ctx.lookupVariable(label) : ctx.lookupVariable(i); + if (value == null) { + throw new UndeclaredVariableException(label); + } + + if (value instanceof NonPackedNode) { + NonPackedNode node = (NonPackedNode) value; + return input.subString(node.getLeftExtent(), node.getRightExtent()); + } // In case of recognizer, we don't have a node. + else if (value instanceof RecognizerResult) { + return input.subString(((RecognizerResult) value).getLeftExtent(), + ((RecognizerResult) value).getRightExtent()); + } else { + throw new UnexpectedTypeOfArgumentException(this); + } + } + + @Override + public java.lang.String toString() { + return i == -1 ? java.lang.String.format("%s.yield", label) + : java.lang.String.format("%s:%d.yield", label, i); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (!(obj instanceof Yield)) return false; + Yield other = (Yield) obj; + return this.label.equals(other.label) && this.i == other.i; + } + + @Override + public int hashCode() { + return Objects.hash(this.label, this.i); + } + + @Override + public T accept(IAbstractASTVisitor visitor) { + return visitor.visit(this); + } + } + + public static class Val extends Expression { + + public static java.lang.String format = "%s.val"; + + private final java.lang.String label; + + Val(java.lang.String label) { + this.label = label; + } + + public java.lang.String getLabel() { + return label; + } + + @Override + public Object interpret(IEvaluatorContext ctx, Input input) { + Object value = ctx.lookupVariable(label); + if (value == null) { + throw new UndeclaredVariableException(label); + } + + if (value instanceof NonterminalNodeWithValue) { + NonterminalNodeWithValue node = (NonterminalNodeWithValue) value; + return node.getValue(); + } // In case of recognizer, we don't have a node. + else if (value instanceof RecognizerResult) { + return ((RecognizerResult) value).getValue(); + } else { + throw new UnexpectedTypeOfArgumentException(this); + } + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (!(obj instanceof Val)) return false; + Val other = (Val) obj; + return this.label.equals(other.label); + } + + @Override + public int hashCode() { + return this.label.hashCode(); + } + + @Override + public java.lang.String toString() { + return java.lang.String.format("%s.val", label); + } + + @Override + public T accept(IAbstractASTVisitor visitor) { + return visitor.visit(this); + } + } + + public static class EndOfFile extends Expression { + + private final Expression index; + + EndOfFile(Expression index) { + this.index = index; + } + + public Expression getIndex() { + return index; + } + + @Override + public Object interpret(IEvaluatorContext ctx, Input input) { + int index = (java.lang.Integer) this.index.interpret(ctx, input); + int length = input.length(); + return length == index + 1; + } + + @Override + public java.lang.String toString() { + return java.lang.String.format("$(%s)", index); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (!(obj instanceof EndOfFile)) return false; + EndOfFile other = (EndOfFile) obj; + return this.index.equals(other.index); + } + + @Override + public int hashCode() { + return this.index.hashCode(); + } + + @Override + public T accept(IAbstractASTVisitor visitor) { + return visitor.visit(this); + } + + } + + public static class IfThenElse extends Expression { + + private final Expression condition; + private final Expression thenPart; + private final Expression elsePart; + + IfThenElse(Expression condition, Expression thenPart, Expression elsePart) { + this.condition = condition; + this.thenPart = thenPart; + this.elsePart = elsePart; + } + + public Expression getCondition() { + return condition; + } + + public Expression getThenPart() { + return thenPart; + } + + public Expression getElsePart() { + return elsePart; + } + + @Override + public Object interpret(IEvaluatorContext ctx, Input input) { + boolean cond = (java.lang.Boolean) condition.interpret(ctx, input); + if (cond) + return thenPart.interpret(ctx, input); + else + return elsePart.interpret(ctx, input); + } + + @Override + public java.lang.String toString() { + return java.lang.String.format("(%s)? %s : %s", condition, thenPart, elsePart); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (!(obj instanceof IfThenElse)) return false; + IfThenElse other = (IfThenElse) obj; + return this.condition.equals(other.condition) + && this.thenPart.equals(other.thenPart) + && this.elsePart.equals(other.elsePart); + } + + @Override + public int hashCode() { + return Objects.hash(this.condition, this.thenPart, this.elsePart); + } + + @Override + public T accept(IAbstractASTVisitor visitor) { + return visitor.visit(this); + } + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/datadependent/ast/Statement.java b/benchmarks/src/main/kotlin/org/iguana/datadependent/ast/Statement.java new file mode 100644 index 000000000..13ec12258 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/datadependent/ast/Statement.java @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.datadependent.ast; + +import org.iguana.datadependent.env.IEvaluatorContext; +import org.iguana.datadependent.traversal.IAbstractASTVisitor; +import org.iguana.utils.input.Input; + +import java.util.Objects; + +public abstract class Statement extends AbstractAST { + + public static class Expression extends Statement { + + private final org.iguana.datadependent.ast.Expression exp; + + Expression(org.iguana.datadependent.ast.Expression exp) { + this.exp = exp; + } + + public org.iguana.datadependent.ast.Expression getExpression() { + return exp; + } + + @Override + public Object interpret(IEvaluatorContext ctx, Input input) { + exp.interpret(ctx, input); + return null; + } + + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Expression)) return false; + Expression other = (Expression) o; + return Objects.equals(exp, other.exp); + } + + @Override + public int hashCode() { + return Objects.hashCode(exp); + } + + @Override + public String toString() { + return exp.toString(); + } + + @Override + public T accept(IAbstractASTVisitor visitor) { + return visitor.visit(this); + } + + } + + public static class VariableDeclaration extends Statement { + + private final org.iguana.datadependent.ast.VariableDeclaration decl; + + VariableDeclaration(org.iguana.datadependent.ast.VariableDeclaration decl) { + this.decl = decl; + } + + public org.iguana.datadependent.ast.VariableDeclaration getDeclaration() { + return decl; + } + + @Override + public Object interpret(IEvaluatorContext ctx, Input input) { + decl.interpret(ctx, input); + return null; + } + + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof VariableDeclaration)) return false; + VariableDeclaration other = (VariableDeclaration) o; + return Objects.equals(decl, other.decl); + } + + @Override + public int hashCode() { + return Objects.hashCode(decl); + } + + @Override + public String toString() { + return decl.toString(); + } + + @Override + public T accept(IAbstractASTVisitor visitor) { + return visitor.visit(this); + } + + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/datadependent/ast/VariableDeclaration.java b/benchmarks/src/main/kotlin/org/iguana/datadependent/ast/VariableDeclaration.java new file mode 100644 index 000000000..f6c54eef7 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/datadependent/ast/VariableDeclaration.java @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.datadependent.ast; + +import org.iguana.datadependent.env.IEvaluatorContext; +import org.iguana.datadependent.traversal.IAbstractASTVisitor; +import org.iguana.utils.input.Input; + +import java.util.Objects; + +public class VariableDeclaration extends AbstractAST { + + public static Object defaultValue = new Object() {}; + + private final String name; + private final int i; + private final Expression expression; + + VariableDeclaration(String name, int i, Expression expression) { + this.name = name; + this.i = i; + this.expression = expression; + } + + VariableDeclaration(String name, Expression expression) { + this(name, -1, expression); + } + + VariableDeclaration(String name, int i) { + this(name, i, null); + } + + VariableDeclaration(String name) { + this(name, null); + } + + public String getName() { + return name; + } + + public Expression getExpression() { + return expression; + } + + @Override + public Object interpret(IEvaluatorContext ctx, Input input) { + Object value = defaultValue; + + if (expression != null) + value = expression.interpret(ctx, input); + + if (i != -1) + ctx.declareVariable(value); + else + ctx.declareVariable(name, value); + + return null; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof VariableDeclaration)) return false; + VariableDeclaration that = (VariableDeclaration) o; + return i == that.i && Objects.equals(name, that.name) && Objects.equals(expression, that.expression); + } + + @Override + public int hashCode() { + return Objects.hash(name, i, expression); + } + + @Override + public String toString() { + return expression != null ? (i != -1 ? String.format( "var %s:%s = %s", name, i, expression) + : String.format( "var %s = %s", name, expression)) + : (i != -1 ? String.format("var %s:%s", name, i) : String.format("var %s", name)); + } + + @Override + public T accept(IAbstractASTVisitor visitor) { + return visitor.visit(this); + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/datadependent/attrs/AbstractAttrs.java b/benchmarks/src/main/kotlin/org/iguana/datadependent/attrs/AbstractAttrs.java new file mode 100644 index 000000000..8b6accdd8 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/datadependent/attrs/AbstractAttrs.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.datadependent.attrs; + + +import io.usethesource.capsule.Set; + +public abstract class AbstractAttrs implements Attr { + + private Set.Immutable env; + + @Override + public Set.Immutable getEnv() { + return env == null ? Set.Immutable.of() : env; + } + + @Override + public void setEnv(Set.Immutable env) { + this.env = env; + } + + @Override + public void setEmpty() { + this.env = io.usethesource.capsule.Set.Immutable.of(); + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/datadependent/attrs/Attr.java b/benchmarks/src/main/kotlin/org/iguana/datadependent/attrs/Attr.java new file mode 100644 index 000000000..e9029158e --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/datadependent/attrs/Attr.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.datadependent.attrs; + +import io.usethesource.capsule.Set; + +public interface Attr { + + Set.Immutable getEnv(); + + void setEnv(Set.Immutable env); + + void setEmpty(); +} diff --git a/benchmarks/src/main/kotlin/org/iguana/datadependent/env/AbstractEvaluatorContext.java b/benchmarks/src/main/kotlin/org/iguana/datadependent/env/AbstractEvaluatorContext.java new file mode 100644 index 000000000..00612fc0c --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/datadependent/env/AbstractEvaluatorContext.java @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.datadependent.env; + +import java.util.HashMap; +import java.util.Map; + +public abstract class AbstractEvaluatorContext implements IEvaluatorContext { + + private Environment env; + + private Map global; + + @Override + public Environment getEnvironment() { + return env; + } + + @Override + public void setEnvironment(Environment env) { + this.env = env; + } + + + @Override + public void popEnvironment() { + env = env.pop(); + } + + @Override + public void pushEnvironment() { + env = env.push(); + } + + @Override + public void declareVariable(String name, Object value) { + env = env._declare(name, value); + } + + @Override + public void declareVariables(String[] names, Object[] values) { + env = env.declare(names, values); + } + + @Override + public void storeVariable(String name, Object value) { + env = env.store(name, value); + } + + @Override + public Object lookupVariable(String name) { + return env.lookup(name); + } + + @Override + public void declareGlobalVariable(String name, Object value) { + if (global == null) + global = new HashMap<>(); + + global.put(name, value); + } + + @Override + public void declareGlobalVariables(String[] names, Object[] values) { + assert names.length == values.length; + + if (names.length == 0) + return; + + if (global == null) + global = new HashMap<>(); + + int i = 0; + while (i < names.length) { + global.put(names[i], values[i]); + i++; + } + } + + @Override + public Object lookupGlobalVariable(String name) { + if (global == null) + return null; + + return global.get(name); + } + + @Override + public void declareVariable(Object value) { + env = env._declare(value); + } + + @Override + public void declareVariables(Object[] values) { + env = env.declare(values); + } + + @Override + public void storeVariable(int i, Object value) { + env = env.store(i, value); + } + + @Override + public Object lookupVariable(int i) { + return env.lookup(i); + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/datadependent/env/Environment.java b/benchmarks/src/main/kotlin/org/iguana/datadependent/env/Environment.java new file mode 100644 index 000000000..924160467 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/datadependent/env/Environment.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.datadependent.env; + + +public interface Environment { + + boolean isEmpty(); + + Environment pop(); + + Environment push(); + + Environment _declare(String name, Object value); + + Environment declare(String[] names, Object[] values); + + Environment store(String name, Object value); + + Object lookup(String name); + + Environment _declare(Object value); + + Environment declare(Object[] values); + + Environment store(int i, Object value); + + Object lookup(int i); + + default int size() { + return 0; + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/datadependent/env/EnvironmentPool.java b/benchmarks/src/main/kotlin/org/iguana/datadependent/env/EnvironmentPool.java new file mode 100644 index 000000000..e52de4ef4 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/datadependent/env/EnvironmentPool.java @@ -0,0 +1,39 @@ +package org.iguana.datadependent.env; + +import java.util.ArrayDeque; +import java.util.Deque; + +public class EnvironmentPool { + + @SuppressWarnings({"rawtypes", "unchecked"}) + static Deque[] environmentPools = new ArrayDeque[3]; + + static { + for (int i = 0; i < environmentPools.length; i++) { + environmentPools[i] = new ArrayDeque<>(1000); + } + } + + public static void clean() { + for (int i = 0; i < environmentPools.length; i++) { + environmentPools[i].clear(); + } + } + + public static Environment get(int size) { + if (size >= 1 && size <= 3) { + Deque environmentPool = environmentPools[size - 1]; + if (!environmentPool.isEmpty()) { + return environmentPool.pop(); + } + } + return null; + } + + public static void returnToPool(Environment env) { + int size = env.size(); + if (size >= 1 && size <= 3) { + environmentPools[size - 1].push(env); + } + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/datadependent/env/GLLEvaluator.java b/benchmarks/src/main/kotlin/org/iguana/datadependent/env/GLLEvaluator.java new file mode 100644 index 000000000..b7fb922a9 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/datadependent/env/GLLEvaluator.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.datadependent.env; + +import org.iguana.datadependent.env.array.ArrayEvaluatorContext; +import org.iguana.datadependent.env.intarray.IntArrayEvaluatorContext; +import org.iguana.datadependent.env.persistent.PersistentEvaluatorContext; +import org.iguana.datadependent.env.simple.SimpleEvaluatorContext; +import org.iguana.util.Configuration; + +public class GLLEvaluator { + + public static IEvaluatorContext getDefaultEvaluatorContext() { + // return new PersistentEvaluatorContext(input); + // return new SimpleEvaluatorContext(input); + return new ArrayEvaluatorContext(); + } + + public static IEvaluatorContext getEvaluatorContext(Configuration config) { + switch (config.getEnvImpl()) { + case ARRAY: + return new ArrayEvaluatorContext(); + case INT_ARRAY: + return new IntArrayEvaluatorContext(); + case HASH_MAP: + return new SimpleEvaluatorContext(); + case TRIE: + return new PersistentEvaluatorContext(); + default: + throw new RuntimeException("Should not have happened!"); + } + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/datadependent/env/IEvaluatorContext.java b/benchmarks/src/main/kotlin/org/iguana/datadependent/env/IEvaluatorContext.java new file mode 100644 index 000000000..9ce3d645c --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/datadependent/env/IEvaluatorContext.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.datadependent.env; + +public interface IEvaluatorContext { + + Environment getEnvironment(); + + void setEnvironment(Environment env); + + Environment getEmptyEnvironment(); + + + // Operations on environment + + void popEnvironment(); + + void pushEnvironment(); + + void declareVariable(String name, Object value); + + void declareVariables(String[] names, Object[] values); + + void storeVariable(String name, Object value); + + Object lookupVariable(String name); + + void declareVariable(Object value); + + void declareVariables(Object[] values); + + void storeVariable(int i, Object value); + + Object lookupVariable(int i); + + // Global parser environment + + void declareGlobalVariable(String name, Object value); + + void declareGlobalVariables(String[] names, Object[] values); + + Object lookupGlobalVariable(String name); + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/datadependent/env/array/ArrayEnvironment.java b/benchmarks/src/main/kotlin/org/iguana/datadependent/env/array/ArrayEnvironment.java new file mode 100644 index 000000000..ed219304f --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/datadependent/env/array/ArrayEnvironment.java @@ -0,0 +1,187 @@ +package org.iguana.datadependent.env.array; + +import org.iguana.datadependent.env.Environment; +import org.iguana.datadependent.env.EnvironmentPool; + +import java.util.Arrays; + +public class ArrayEnvironment implements Environment { + + static final ArrayEnvironment EMPTY = new ArrayEnvironment(new Object[0], 0); + + private final Object[] values; + private int hashCode; + + private ArrayEnvironment(Object[] values, int hashCode) { + this.values = values; + this.hashCode = hashCode; + } + + private void init(Object[] oldValues, Object value, int oldHashCode) { + int length = oldValues.length; + System.arraycopy(this.values, 0, values, 0, length); + + values[length] = value; + + int valueHashCode = oldHashCode; + if (value instanceof Object[]) { + for (int i = 0; i < ((Object[]) value).length; i++) { + Object element = ((Object[]) value)[i]; + valueHashCode = 31 * valueHashCode + element.hashCode(); + } + } else { + valueHashCode = 31 * valueHashCode + value.hashCode(); + } + + this.hashCode = valueHashCode; + } + + @Override + public boolean isEmpty() { + return values.length == 0; + } + + @Override + public Environment pop() { + throw new RuntimeException("Unsupported with this type of environment!"); + } + + @Override + public Environment push() { + throw new RuntimeException("Unsupported with this type of environment!"); + } + + @Override + public Environment _declare(String name, Object value) { + throw new RuntimeException("Unsupported with this type of environment!"); + } + + @Override + public Environment declare(String[] names, Object[] values) { + throw new RuntimeException("Unsupported with this type of environment!"); + } + + @Override + public Environment store(String name, Object value) { + throw new RuntimeException("Unsupported with this type of environment!"); + } + + @Override + public Object lookup(String name) { + throw new RuntimeException("Unsupported with this type of environment!"); + } + + @Override + public int hashCode() { + return hashCode; + } + + @Override + public boolean equals(Object other) { + + if (this == other) + return true; + + if (!(other instanceof ArrayEnvironment)) + return false; + + if (this.hashCode() != other.hashCode()) + return false; + + ArrayEnvironment that = (ArrayEnvironment) other; + + if (values.length != that.values.length) + return false; + + for (int i = 0; i < values.length; i++) + if (!values[i].equals(that.values[i])) + return false; + + return true; + } + + @Override + public String toString() { + return Arrays.toString(values); + } + + @Override + public Environment _declare(Object value) { + int length = this.values.length; + + ArrayEnvironment environment = (ArrayEnvironment) EnvironmentPool.get(length + 1); + if (environment != null) { + environment.init(this.values, value, this.hashCode); + return environment; + } + + Object[] values = new Object[length + 1]; + + if (length != 0) + System.arraycopy(this.values, 0, values, 0, length); + + values[length] = value; + + int valueHashCode = hashCode; + if (value instanceof Object[]) { + for (int i = 0; i < ((Object[]) value).length; i++) { + Object element = ((Object[]) value)[i]; + valueHashCode = 31 * valueHashCode + element.hashCode(); + } + } else { + valueHashCode = 31 * valueHashCode + value.hashCode(); + } + + return new ArrayEnvironment(values, valueHashCode); + } + + @Override + public Environment declare(Object[] values) { // Assuming values.length != 0 + int length = this.values.length; + Object[] vals = new Object[length + values.length]; + + if (length != 0) + System.arraycopy(this.values, 0, vals, 0, length); + + int hashCode = this.hashCode; + + int j = 0; + for (int i = length; i < length + values.length; i++) { + vals[i] = values[j]; + hashCode = hashCode + 31 * values[j].hashCode(); + j++; + } + + return new ArrayEnvironment(vals, hashCode); + } + + @Override + public Environment store(int i, Object value) { + Object[] values = new Object[this.values.length]; + + int hashCode = 0; + for (int j = 0; j < values.length; j++) { + + if (i != j) + values[i] = this.values[i]; + else + values[i] = value; + + if (values[i] != null) + hashCode = hashCode + values[i].hashCode(); + } + + return new ArrayEnvironment(values, hashCode); + } + + @Override + public Object lookup(int i) { + return values[i]; + } + + @Override + public int size() { + return values.length; + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/datadependent/env/array/ArrayEvaluatorContext.java b/benchmarks/src/main/kotlin/org/iguana/datadependent/env/array/ArrayEvaluatorContext.java new file mode 100644 index 000000000..274109835 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/datadependent/env/array/ArrayEvaluatorContext.java @@ -0,0 +1,17 @@ +package org.iguana.datadependent.env.array; + +import org.iguana.datadependent.env.AbstractEvaluatorContext; +import org.iguana.datadependent.env.Environment; + +public class ArrayEvaluatorContext extends AbstractEvaluatorContext { + + public ArrayEvaluatorContext() { + setEnvironment(ArrayEnvironment.EMPTY); + } + + @Override + public Environment getEmptyEnvironment() { + return ArrayEnvironment.EMPTY; + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/datadependent/env/intarray/IntArrayEnvironment.java b/benchmarks/src/main/kotlin/org/iguana/datadependent/env/intarray/IntArrayEnvironment.java new file mode 100644 index 000000000..8233203d4 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/datadependent/env/intarray/IntArrayEnvironment.java @@ -0,0 +1,197 @@ +package org.iguana.datadependent.env.intarray; + +import org.iguana.datadependent.env.Environment; +import org.iguana.datadependent.env.EnvironmentPool; + +import static java.lang.Integer.toUnsignedLong; + +public class IntArrayEnvironment implements Environment { + + static final IntArrayEnvironment EMPTY = new IntArrayEnvironment(0); + + /** + * size [o6, o5] [o4, o3] o2 o1 + */ + private long value; + + private IntArrayEnvironment(long value) { + this.value = value; + } + + public void init(long value) { + this.value = value; + } + + @Override + public boolean isEmpty() { + return size() == 0; + } + + @Override + public Environment pop() { + throw new RuntimeException("Unsupported with this type of environment!"); + } + + @Override + public Environment push() { + throw new RuntimeException("Unsupported with this type of environment!"); + } + + @Override + public Environment _declare(String name, Object value) { + throw new RuntimeException("Unsupported with this type of environment!"); + } + + @Override + public IntArrayEnvironment declare(String[] names, Object[] values) { + throw new RuntimeException("Unsupported with this type of environment!"); + } + + @Override + public IntArrayEnvironment store(String name, Object value) { + throw new RuntimeException("Unsupported with this type of environment!"); + } + + @Override + public Object lookup(String name) { + throw new RuntimeException("Unsupported with this type of environment!"); + } + + @Override + public IntArrayEnvironment _declare(Object value) { + int size = size(); + + long result; + + if (value instanceof Long) { + long longValue = (long) value; + int first = (int) (longValue >>> 32); + int second = (int) (longValue & 0x00000000_FFFFFFFFL); + if (size == 2) { + result = set(2, second, this.value); + result = set(3, first, result); + } else if (size == 3) { + result = set(4, second, this.value); + result = set(5, first, result); + } else { + throw new RuntimeException("Tuple values cannot be added before single integers"); + } + } else { + if (size != 0 && size != 1) + throw new RuntimeException("Integer values can only be added at position 0 or 1"); + result = set(size, (Integer) value, this.value); + } + + result = setSize(size + 1, result); + + IntArrayEnvironment environment = (IntArrayEnvironment) EnvironmentPool.get(size + 1); + if (environment != null) { + environment.init(result); + return environment; + } + + return new IntArrayEnvironment(result); + } + + private static long set(int i, int value, long currentValue) { + if (value > 511) throw new RuntimeException("The value has to be smaller than 512"); + + long bitmask = ~(0x00000000000003FFL << (i * 10)); + + return (currentValue & bitmask) | (toUnsignedLong(value) << (i * 10)); + } + + private long setSize(int size, long currentValue) { + return ((long) size << 60) | (currentValue & 0x0FFFFFFFFFFFFFFFL); + } + + @Override + public IntArrayEnvironment declare(Object[] values) { + if (values.length > 6) throw new RuntimeException("Cannot add more than 6 values in this type of environment"); + + long value = 0L; + + for (int i = 0; i < values.length; i++) { + int currentValue = (Integer) values[i]; + value |= (toUnsignedLong(currentValue) << (i * 10)); + } + + long size = values.length; + value = (size << 60) | (value & 0x0FFFFFFFFFFFFFFFL); + return new IntArrayEnvironment(value); + } + + @Override + public IntArrayEnvironment store(int i, Object value) { + return new IntArrayEnvironment(set(i, (int) value, this.value)); + } + + @Override + public Object lookup(int i) { + if (i == 0 || i == 1) { + return get(i); + } + return getTuple(i); + } + + private int get(int i) { + long bitmask = 0x00000000000003FFL << (i * 10); + return (int) ((this.value & bitmask) >>> (i * 10)); + } + + private long getTuple(int i) { + int first; + int second; + if (i == 2) { + first = get(2); + second = get(3); + } else if (i == 3) { + first = get(4); + second = get(5); + } else { + throw new RuntimeException(); + } + + return toUnsignedLong(second) << 32 | toUnsignedLong(first); + } + + @Override + public int size() { + return (int) ((this.value & 0xF000000000000000L) >>> 60); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof IntArrayEnvironment)) return false; + IntArrayEnvironment other = (IntArrayEnvironment) o; + return value == other.value; + } + + @Override + public int hashCode() { + return Long.hashCode(value); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + + int first = get(0); + int second = get(1); + long firstTuple = getTuple(2); + int third = (int) (firstTuple >> 32); + int fourth = (int) (firstTuple & 0x0000_0000_FFFF_FFF); + long secondTuple = getTuple(3); + int fifth = (int) (secondTuple >> 32); + int sixth = (int) (secondTuple & 0x0000_0000_FFFF_FFF); + + sb.append("{") + .append(first).append(", ") + .append(second).append(", ") + .append("[").append(third).append(", ").append(fourth).append("], ") + .append("[").append(fifth).append(", ").append(sixth).append("]") + .append("}"); + return sb.toString(); + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/datadependent/env/intarray/IntArrayEvaluatorContext.java b/benchmarks/src/main/kotlin/org/iguana/datadependent/env/intarray/IntArrayEvaluatorContext.java new file mode 100644 index 000000000..4c86bfb52 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/datadependent/env/intarray/IntArrayEvaluatorContext.java @@ -0,0 +1,16 @@ +package org.iguana.datadependent.env.intarray; + +import org.iguana.datadependent.env.AbstractEvaluatorContext; +import org.iguana.datadependent.env.Environment; + +public class IntArrayEvaluatorContext extends AbstractEvaluatorContext { + + public IntArrayEvaluatorContext() { + setEnvironment(getEmptyEnvironment()); + } + + @Override + public Environment getEmptyEnvironment() { + return IntArrayEnvironment.EMPTY; + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/datadependent/env/intarray/MutableLong.java b/benchmarks/src/main/kotlin/org/iguana/datadependent/env/intarray/MutableLong.java new file mode 100644 index 000000000..347a3ac3b --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/datadependent/env/intarray/MutableLong.java @@ -0,0 +1,19 @@ +package org.iguana.datadependent.env.intarray; + +public class MutableLong { + + private long value; + + public void setValue(long value) { + this.value = value; + } + + public final int getHigherOrderInt() { + return (int) (value >> 32); + } + + public final int getLowerOrderInt() { + return (int) (value & 0xffffffffL); + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/datadependent/env/persistent/PersistentEnvironment.java b/benchmarks/src/main/kotlin/org/iguana/datadependent/env/persistent/PersistentEnvironment.java new file mode 100644 index 000000000..6f16901ca --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/datadependent/env/persistent/PersistentEnvironment.java @@ -0,0 +1,184 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.datadependent.env.persistent; + +import io.usethesource.capsule.Map; +import org.iguana.datadependent.ast.VariableDeclaration; +import org.iguana.datadependent.env.Environment; +import org.iguana.grammar.exception.UndeclaredVariableException; +import org.iguana.grammar.exception.UndefinedRuntimeValueException; + +public class PersistentEnvironment implements Environment { + + static final Environment EMPTY = new PersistentEnvironment(null, Map.Immutable.of()); + + private final PersistentEnvironment parent; + + private final Map.Immutable bindings; + + private PersistentEnvironment(PersistentEnvironment parent, Map.Immutable bindings) { + this.parent = parent; + this.bindings = bindings; + } + + @Override + public boolean isEmpty() { + return bindings.isEmpty() && (parent == null || parent.isEmpty()); + } + + @Override + public Environment pop() { + return parent; + } + + @Override + public Environment push() { + return new PersistentEnvironment(this, Map.Immutable.of()); + } + + @Override + public Environment _declare(String name, Object value) { + return new PersistentEnvironment(parent, bindings.__put(name, value)); + } + + @Override + public Environment declare(String[] names, Object[] values) { + Map.Immutable bindings = this.bindings; + int i = 0; + while (i < names.length) { + bindings = bindings.__put(names[i], values[i]); + i++; + } + return new PersistentEnvironment(parent, bindings); + } + + @Override + public Environment store(String name, Object value) { + + Object result = bindings.get(name); + + if (result == null) { + + if (parent == null) { + throw new UndeclaredVariableException(name); + } + + Environment parent = this.parent.store(name, value); + + if (parent == this.parent) { + return this; + } + + return new PersistentEnvironment((PersistentEnvironment) parent, bindings); + } + + Map.Immutable bindings = this.bindings.__put(name, value); + if (bindings == this.bindings) { + return this; + } + + return new PersistentEnvironment(parent, bindings); + } + + @Override + public Object lookup(String name) { + + Object value = bindings.get(name); + + if (value == null && parent != null) { + value = parent.lookup(name); + } + + if (value != null) { + + if (value == VariableDeclaration.defaultValue) { + throw UndefinedRuntimeValueException.instance; + } + + return value; + } + + throw new UndeclaredVariableException(name); + } + + @Override + public int hashCode() { + return (parent == null ? 0 : parent.hashCode()) + bindings.hashCode(); + } + + @Override + public boolean equals(Object other) { + if (this == other) return true; + + if (!(other instanceof PersistentEnvironment)) return false; + + PersistentEnvironment that = (PersistentEnvironment) other; + + if (this.bindings == that.bindings || this.bindings.equals(that.bindings)) { + + if (this.parent == that.parent) { + return true; + } + + if (this.parent != null) { + return this.parent.equals(that.parent); + } + + return false; + } + + return false; + } + + @Override + public String toString() { + return (parent != null ? parent.toString() + " -> " : "() -> ") + + (bindings != null ? bindings.toString() : "()"); + } + + @Override + public Environment _declare(Object value) { + throw new RuntimeException("Unsupported with this type of environment!"); + } + + @Override + public Environment declare(Object[] values) { + throw new RuntimeException("Unsupported with this type of environment!"); + } + + @Override + public Environment store(int i, Object value) { + throw new RuntimeException("Unsupported with this type of environment!"); + } + + @Override + public Object lookup(int i) { + throw new RuntimeException("Unsupported with this type of environment!"); + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/datadependent/env/persistent/PersistentEvaluatorContext.java b/benchmarks/src/main/kotlin/org/iguana/datadependent/env/persistent/PersistentEvaluatorContext.java new file mode 100644 index 000000000..d4c996423 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/datadependent/env/persistent/PersistentEvaluatorContext.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.datadependent.env.persistent; + +import org.iguana.datadependent.env.AbstractEvaluatorContext; +import org.iguana.datadependent.env.Environment; + +public class PersistentEvaluatorContext extends AbstractEvaluatorContext { + + public PersistentEvaluatorContext() { + setEnvironment(PersistentEnvironment.EMPTY); + } + + @Override + public Environment getEmptyEnvironment() { + return PersistentEnvironment.EMPTY; + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/datadependent/env/simple/SimpleEvaluatorContext.java b/benchmarks/src/main/kotlin/org/iguana/datadependent/env/simple/SimpleEvaluatorContext.java new file mode 100644 index 000000000..660393815 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/datadependent/env/simple/SimpleEvaluatorContext.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.datadependent.env.simple; + +import org.iguana.datadependent.env.AbstractEvaluatorContext; +import org.iguana.datadependent.env.Environment; + +public class SimpleEvaluatorContext extends AbstractEvaluatorContext { + + public SimpleEvaluatorContext() { + setEnvironment(SimpleImmutableEnvironment.EMPTY); + } + + @Override + public Environment getEmptyEnvironment() { + return SimpleImmutableEnvironment.EMPTY; + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/datadependent/env/simple/SimpleImmutableEnvironment.java b/benchmarks/src/main/kotlin/org/iguana/datadependent/env/simple/SimpleImmutableEnvironment.java new file mode 100644 index 000000000..a158a8e71 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/datadependent/env/simple/SimpleImmutableEnvironment.java @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.datadependent.env.simple; + +import org.iguana.datadependent.ast.VariableDeclaration; +import org.iguana.datadependent.env.Environment; +import org.iguana.grammar.exception.UndeclaredVariableException; +import org.iguana.grammar.exception.UndefinedRuntimeValueException; + +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Collectors; + +import static org.iguana.utils.string.StringUtil.listToString; + + +public class SimpleImmutableEnvironment implements Environment { + + static final Environment EMPTY = new SimpleImmutableEnvironment(null, new HashMap<>(), 0); + + private final SimpleImmutableEnvironment parent; + + private final Map bindings; + + private final int hashCode; + + private SimpleImmutableEnvironment(SimpleImmutableEnvironment parent, Map bindings, int hashCode) { + this.parent = parent; + this.bindings = bindings; + this.hashCode = hashCode; + } + + @Override + public boolean isEmpty() { + return bindings.isEmpty() && (parent == null || parent.isEmpty()); + } + + @Override + public Environment pop() { + return parent; + } + + @Override + public Environment push() { + return new SimpleImmutableEnvironment(this, new HashMap<>(), this.hashCode()); + } + + @Override + public Environment _declare(String name, Object value) { + Map bindings = new HashMap<>(this.bindings); + bindings.put(name, value); + return new SimpleImmutableEnvironment(parent, bindings, this.hashCode + (name.hashCode() ^ value.hashCode())); + } + + @Override + public Environment declare(String[] names, Object[] values) { + Map bindings = new HashMap<>(this.bindings); + int i = 0; + int hashCode = this.hashCode; + while (i < names.length) { + bindings.put(names[i], values[i]); + hashCode = hashCode + (names[i].hashCode() ^ values[i].hashCode()); + i++; + } + return new SimpleImmutableEnvironment(parent, bindings, hashCode); + } + + @Override + public Environment store(String name, Object value) { + + Object result = bindings.get(name); + + if (result == null) { + + if (parent == null) + throw new UndeclaredVariableException(name); + + Environment parent = this.parent.store(name, value); + + if (parent == this.parent) + return this; + + int hashCode = parent.hashCode(); + for (Map.Entry entry : this.bindings.entrySet()) + hashCode = hashCode + (entry.getKey().hashCode() ^ entry.getValue().hashCode()); + + return new SimpleImmutableEnvironment((SimpleImmutableEnvironment) parent, bindings, hashCode); + } + + Map bindings = new HashMap<>(this.bindings); + bindings.put(name, value); + + return new SimpleImmutableEnvironment(parent, bindings, hashCode + (name.hashCode() ^ value.hashCode())); + } + + @Override + public Object lookup(String name) { + + Object value = bindings.get(name); + + if (value != null && parent != null) + return parent.lookup(name); + + if (value != null) { + if (value == VariableDeclaration.defaultValue) + throw UndefinedRuntimeValueException.instance; + + return value; + } + + throw new UndeclaredVariableException(name); + } + + @Override + public boolean equals(Object other) { + if (this == other) + return true; + + if (this.hashCode() != other.hashCode()) + return false; + + if (!(other instanceof SimpleImmutableEnvironment)) + return false; + + SimpleImmutableEnvironment that = (SimpleImmutableEnvironment) other; + + if (bindings == that.bindings || bindings.equals(that.bindings)) { + + if (parent == that.parent) + return true; + + if (parent != null) + return parent.equals(that.parent); + } + + return false; + } + + @Override + public int hashCode() { + return hashCode; + } + + @Override + public String toString() { + return (parent != null ? parent + " -> " : "() -> ") + + (bindings != null + ? listToString(bindings.entrySet().stream().map(entry -> entry.getKey() + " : " + entry.getValue()) + .collect(Collectors.toList()), ";") + : "()"); + } + + @Override + public Environment _declare(Object value) { + throw new RuntimeException("Unsupported with this type of environment!"); + } + + @Override + public Environment declare(Object[] values) { + throw new RuntimeException("Unsupported with this type of environment!"); + } + + @Override + public Environment store(int i, Object value) { + throw new RuntimeException("Unsupported with this type of environment!"); + } + + @Override + public Object lookup(int i) { + throw new RuntimeException("Unsupported with this type of environment!"); + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/datadependent/traversal/FreeVariableVisitor.java b/benchmarks/src/main/kotlin/org/iguana/datadependent/traversal/FreeVariableVisitor.java new file mode 100644 index 000000000..3a585205c --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/datadependent/traversal/FreeVariableVisitor.java @@ -0,0 +1,847 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.datadependent.traversal; + +import org.iguana.datadependent.ast.Expression.Add; +import org.iguana.datadependent.ast.Expression.And; +import org.iguana.datadependent.ast.Expression.AndIndent; +import org.iguana.datadependent.ast.Expression.Assignment; +import org.iguana.datadependent.ast.Expression.BinaryExpression; +import org.iguana.datadependent.ast.Expression.Boolean; +import org.iguana.datadependent.ast.Expression.Call; +import org.iguana.datadependent.ast.Expression.Divide; +import org.iguana.datadependent.ast.Expression.EndOfFile; +import org.iguana.datadependent.ast.Expression.Equal; +import org.iguana.datadependent.ast.Expression.Greater; +import org.iguana.datadependent.ast.Expression.GreaterThanEqual; +import org.iguana.datadependent.ast.Expression.Integer; +import org.iguana.datadependent.ast.Expression.LShiftANDEqZero; +import org.iguana.datadependent.ast.Expression.LeftExtent; +import org.iguana.datadependent.ast.Expression.Less; +import org.iguana.datadependent.ast.Expression.LessThanEqual; +import org.iguana.datadependent.ast.Expression.Multiply; +import org.iguana.datadependent.ast.Expression.Name; +import org.iguana.datadependent.ast.Expression.Not; +import org.iguana.datadependent.ast.Expression.NotEqual; +import org.iguana.datadependent.ast.Expression.Or; +import org.iguana.datadependent.ast.Expression.OrIndent; +import org.iguana.datadependent.ast.Expression.Real; +import org.iguana.datadependent.ast.Expression.RightExtent; +import org.iguana.datadependent.ast.Expression.String; +import org.iguana.datadependent.ast.Expression.Subtract; +import org.iguana.datadependent.ast.Expression.Tuple; +import org.iguana.datadependent.ast.Expression.Val; +import org.iguana.datadependent.ast.Expression.Yield; +import org.iguana.datadependent.ast.Statement; +import org.iguana.datadependent.ast.Statement.Expression; +import org.iguana.datadependent.ast.VariableDeclaration; +import org.iguana.grammar.condition.Condition; +import org.iguana.grammar.condition.DataDependentCondition; +import org.iguana.grammar.condition.PositionalCondition; +import org.iguana.grammar.condition.RegularExpressionCondition; +import org.iguana.grammar.runtime.RuntimeRule; +import org.iguana.grammar.symbol.Align; +import org.iguana.grammar.symbol.Alt; +import org.iguana.grammar.symbol.Block; +import org.iguana.grammar.symbol.Code; +import org.iguana.grammar.symbol.Conditional; +import org.iguana.grammar.symbol.Error; +import org.iguana.grammar.symbol.Group; +import org.iguana.grammar.symbol.IfThen; +import org.iguana.grammar.symbol.IfThenElse; +import org.iguana.grammar.symbol.Ignore; +import org.iguana.grammar.symbol.Nonterminal; +import org.iguana.grammar.symbol.Offside; +import org.iguana.grammar.symbol.Opt; +import org.iguana.grammar.symbol.Plus; +import org.iguana.grammar.symbol.Return; +import org.iguana.grammar.symbol.Star; +import org.iguana.grammar.symbol.Start; +import org.iguana.grammar.symbol.Symbol; +import org.iguana.grammar.symbol.Terminal; +import org.iguana.grammar.symbol.While; +import org.iguana.traversal.IConditionVisitor; +import org.iguana.traversal.ISymbolVisitor; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + + +public class FreeVariableVisitor implements IAbstractASTVisitor, ISymbolVisitor, IConditionVisitor { + + /* + * Free variables of a symbol and updates to free variables + */ + private final Set freeVariables; + private final Set updates; + + + /* + * State variable bindings of nonterminals in the rule + */ + private final Map> nonterminal_uses; + // Given assignments to state variables within a nonterminal + private final Map> nonterminal_updates; + // Given uses of updated state variables of a nonterminal after a use of a nonterminal in all the rules + private final Map> nonterminal_returns; + // Given uses of updated state variables of a nonterminal after a use of the nonterminal in the current rule + private Map> nonterminal_bindings; + + public FreeVariableVisitor(Set freeVariables) { + this(freeVariables, null); + } + + public FreeVariableVisitor(Set freeVariables, Set updates) { + this.freeVariables = freeVariables; + this.updates = updates; + this.nonterminal_uses = null; + this.nonterminal_updates = null; + this.nonterminal_returns = null; + this.nonterminal_bindings = null; + } + + public FreeVariableVisitor( + Map> nonterminal_uses, + Map> nonterminal_updates, + Map> nonterminal_returns) { + this.freeVariables = null; + this.updates = null; + this.nonterminal_uses = nonterminal_uses; + this.nonterminal_updates = nonterminal_updates; + this.nonterminal_returns = nonterminal_returns; + this.nonterminal_bindings = new HashMap<>(); + } + + public void compute(RuntimeRule rule) { + io.usethesource.capsule.Set.Immutable env = io.usethesource.capsule.Set.Immutable.of(); + + List parameters = rule.getHead().getParameters(); + if (parameters != null) { + for (java.lang.String parameter : parameters) + env = env.__insert(parameter); + } + + io.usethesource.capsule.Set.Immutable _env = env; + + for (Symbol symbol : rule.getBody()) { + + symbol.setEnv(_env); + this.visitSymbol(symbol); + _env = symbol.getEnv(); + + symbol.setEmpty(); + } + + } + + public Map> computeBindings(RuntimeRule rule) { + initBindings(); + compute(rule); + return nonterminal_bindings; + } + + private void initBindings() { + this.nonterminal_bindings = new HashMap<>(); + } + + @Override + public Void visit(Boolean expression) { + return null; + } + + @Override + public Void visit(Integer expression) { + return null; + } + + @Override + public Void visit(Real expression) { + return null; + } + + @Override + public Void visit(String expression) { + return null; + } + + @Override + public Void visit(Not not) { + org.iguana.datadependent.ast.Expression expression = not.getExp(); + + expression.setEnv(not.getEnv()); + expression.accept(this); + return null; + } + + @Override + public Void visit(Tuple expression) { + + for (org.iguana.datadependent.ast.Expression element : expression.getElements()) { + element.setEnv(expression.getEnv()); + element.accept(this); + } + + return null; + } + + @Override + public Void visit(Name expression) { + java.lang.String name = expression.getName(); + + if (!expression.getEnv().contains(name)) + use(name); + + return null; + } + + private void use(java.lang.String name) { + if (freeVariables != null) + freeVariables.add(name); + + if (nonterminal_bindings != null && !nonterminal_bindings.isEmpty()) { + for (Nonterminal nonterminal : nonterminal_bindings.keySet()) { + if (nonterminal_updates.get(nonterminal).contains(name)) { + nonterminal_returns.get(nonterminal).add(name); + nonterminal_bindings.get(nonterminal).add(name); + } + } + } + } + + @Override + public Void visit(Call expression) { + + for (org.iguana.datadependent.ast.Expression argument : expression.getArguments()) { + argument.setEnv(expression.getEnv()); + argument.accept(this); + } + + return null; + } + + @Override + public Void visit(Assignment expression) { + + java.lang.String id = expression.getId(); + org.iguana.datadependent.ast.Expression exp = expression.getExpression(); + + if (!expression.getEnv().contains(id)) { + use(id); + update(id); + } + + exp.setEnv(expression.getEnv()); + exp.accept(this); + + return null; + } + + private void update(java.lang.String name) { + if (updates != null) + updates.add(name); + } + + @Override + public Void visit(LShiftANDEqZero expression) { + + org.iguana.datadependent.ast.Expression lhs = expression.getLhs(); + + lhs.setEnv(expression.getEnv()); + lhs.accept(this); + + org.iguana.datadependent.ast.Expression rhs = expression.getRhs(); + + rhs.setEnv(expression.getEnv()); + rhs.accept(this); + + return null; + } + + @Override + public Void visit(OrIndent expression) { + + org.iguana.datadependent.ast.Expression index = expression.getIndex(); + + index.setEnv(expression.getEnv()); + index.accept(this); + + org.iguana.datadependent.ast.Expression ind = expression.getIndent(); + + ind.setEnv(expression.getEnv()); + ind.accept(this); + + org.iguana.datadependent.ast.Expression first = expression.getFirst(); + + first.setEnv(expression.getEnv()); + first.accept(this); + + org.iguana.datadependent.ast.Expression lExt = expression.getLExt(); + + lExt.setEnv(expression.getEnv()); + lExt.accept(this); + + return null; + } + + @Override + public Void visit(AndIndent expression) { + + org.iguana.datadependent.ast.Expression index = expression.getIndex(); + + index.setEnv(expression.getEnv()); + index.accept(this); + + org.iguana.datadependent.ast.Expression first = expression.getFirst(); + + first.setEnv(expression.getEnv()); + first.accept(this); + + org.iguana.datadependent.ast.Expression lExt = expression.getLExt(); + + lExt.setEnv(expression.getEnv()); + lExt.accept(this); + + return null; + } + + @Override + public Void visit(Or expression) { + + org.iguana.datadependent.ast.Expression lhs = expression.getLhs(); + + lhs.setEnv(expression.getEnv()); + lhs.accept(this); + + org.iguana.datadependent.ast.Expression rhs = expression.getRhs(); + + rhs.setEnv(expression.getEnv()); + rhs.accept(this); + + return null; + } + + @Override + public Void visit(Add expression) { + return visitBinaryExpression(expression); + } + + @Override + public Void visit(Subtract expression) { + return visitBinaryExpression(expression); + } + + @Override + public Void visit(Multiply expression) { + return visitBinaryExpression(expression); + } + + @Override + public Void visit(Divide expression) { + return visitBinaryExpression(expression); + } + + private Void visitBinaryExpression(BinaryExpression expression) { + org.iguana.datadependent.ast.Expression lhs = expression.getLhs(); + + lhs.setEnv(expression.getEnv()); + lhs.accept(this); + + org.iguana.datadependent.ast.Expression rhs = expression.getRhs(); + + rhs.setEnv(expression.getEnv()); + rhs.accept(this); + + return null; + } + + @Override + public Void visit(And expression) { + return visitBinaryExpression(expression); + } + + @Override + public Void visit(Less expression) { + return visitBinaryExpression(expression); + } + + @Override + public Void visit(LessThanEqual expression) { + return visitBinaryExpression(expression); + } + + @Override + public Void visit(Greater expression) { + return visitBinaryExpression(expression); + } + + @Override + public Void visit(GreaterThanEqual expression) { + return visitBinaryExpression(expression); + } + + @Override + public Void visit(Equal expression) { + return visitBinaryExpression(expression); + } + + @Override + public Void visit(NotEqual expression) { + return visitBinaryExpression(expression); + } + + @Override + public Void visit(LeftExtent expression) { + + java.lang.String name = java.lang.String.format(org.iguana.datadependent.ast.Expression.LeftExtent.format, + expression.getLabel()); + + if (!expression.getEnv().contains(name)) + use(name); + + return null; + } + + @Override + public Void visit(RightExtent expression) { + + java.lang.String label = expression.getLabel(); + + if (!expression.getEnv().contains(label)) + use(label); + + return null; + } + + @Override + public Void visit(Yield expression) { + + java.lang.String label = expression.getLabel(); + + if (!expression.getEnv().contains(label)) + use(label); + + return null; + } + + @Override + public Void visit(Val expression) { + + java.lang.String label = expression.getLabel(); + + if (!expression.getEnv().contains(label)) + use(label); + + return null; + } + + @Override + public Void visit(EndOfFile expression) { + + org.iguana.datadependent.ast.Expression index = expression.getIndex(); + + index.setEnv(expression.getEnv()); + index.accept(this); + + return null; + } + + @Override + public Void visit(org.iguana.datadependent.ast.Expression.IfThenElse expression) { + + org.iguana.datadependent.ast.Expression condition = expression.getCondition(); + condition.setEnv(expression.getEnv()); + condition.accept(this); + + org.iguana.datadependent.ast.Expression thenPart = expression.getThenPart(); + thenPart.setEnv(expression.getEnv()); + thenPart.accept(this); + + org.iguana.datadependent.ast.Expression elsePart = expression.getElsePart(); + elsePart.setEnv(expression.getEnv()); + elsePart.accept(this); + + return null; + } + + @Override + public Void visit(VariableDeclaration declaration) { + + org.iguana.datadependent.ast.Expression expression = declaration.getExpression(); + + if (expression != null) { + expression.setEnv(declaration.getEnv()); + expression.accept(this); + } + + declaration.setEnv(declaration.getEnv().__insert(declaration.getName())); + + return null; + } + + @Override + public Void visit(org.iguana.datadependent.ast.Statement.VariableDeclaration statement) { + + VariableDeclaration declaration = statement.getDeclaration(); + + declaration.setEnv(statement.getEnv()); + declaration.accept(this); + + statement.setEnv(declaration.getEnv()); + + return null; + } + + @Override + public Void visit(Expression statement) { + + org.iguana.datadependent.ast.Expression expression = statement.getExpression(); + + expression.setEnv(statement.getEnv()); + expression.accept(this); + + return null; + } + + @Override + public Void visit(Align symbol) { + Symbol sym = symbol.getSymbol(); + + sym.setEnv(symbol.getEnv()); + visitSymbol(sym); + + symbol.setEnv(sym.getEnv()); + + return null; + } + + @Override + public Void visit(Block symbol) { + + io.usethesource.capsule.Set.Immutable env = symbol.getEnv(); + + for (Symbol sym : symbol.getSymbols()) { + sym.setEnv(env); + visitSymbol(sym); + env = sym.getEnv(); + } + + return null; + } + + @Override + public Void visit(Code symbol) { + + io.usethesource.capsule.Set.Immutable env = symbol.getEnv(); + + Symbol sym = symbol.getSymbol(); + + sym.setEnv(env); + visitSymbol(sym); + env = sym.getEnv(); + + for (Statement statement : symbol.getStatements()) { + statement.setEnv(env); + statement.accept(this); + env = statement.getEnv(); + } + + symbol.setEnv(env); + + return null; + } + + @Override + public Void visit(Error error) { + return null; + } + + @Override + public Void visit(Conditional symbol) { + + Symbol sym = symbol.getSymbol(); + org.iguana.datadependent.ast.Expression expression = symbol.getExpression(); + + io.usethesource.capsule.Set.Immutable env = symbol.getEnv(); + + sym.setEnv(env); + visitSymbol(sym); + + env = sym.getEnv(); + + expression.setEnv(env); + expression.accept(this); + + return null; + } + + @Override + public Void visit(IfThen symbol) { + + org.iguana.datadependent.ast.Expression expression = symbol.getExpression(); + Symbol thenPart = symbol.getThenPart(); + + expression.setEnv(symbol.getEnv()); + expression.accept(this); + + thenPart.setEnv(symbol.getEnv()); + visitSymbol(thenPart); + + return null; + } + + @Override + public Void visit(IfThenElse symbol) { + + org.iguana.datadependent.ast.Expression expression = symbol.getExpression(); + Symbol thenPart = symbol.getThenPart(); + Symbol elsePart = symbol.getElsePart(); + + expression.setEnv(symbol.getEnv()); + expression.accept(this); + + thenPart.setEnv(symbol.getEnv()); + visitSymbol(thenPart); + + elsePart.setEnv(symbol.getEnv()); + visitSymbol(thenPart); + + return null; + } + + @Override + public Void visit(Ignore symbol) { + Symbol sym = symbol.getSymbol(); + + sym.setEnv(symbol.getEnv()); + visitSymbol(sym); + + symbol.setEnv(sym.getEnv()); + + return null; + } + + @Override + public Void visit(Nonterminal symbol) { + + io.usethesource.capsule.Set.Immutable env = symbol.getEnv(); + + if (symbol.getVariable() != null) + env = env.__insert(symbol.getVariable()); + + if (symbol.getArguments() != null) { + for (org.iguana.datadependent.ast.Expression argument : symbol.getArguments()) { + argument.setEnv(env); + argument.accept(this); + env = argument.getEnv(); + } + } + + if (nonterminal_uses != null && nonterminal_uses.containsKey(symbol)) { + for (java.lang.String parameter : nonterminal_uses.get(symbol)) + use(parameter); + } + + if (nonterminal_bindings != null + && !nonterminal_bindings.containsKey(symbol) + && nonterminal_updates.containsKey(symbol)) + nonterminal_bindings.put(symbol, new HashSet<>()); + + symbol.setEnv(env); + + return null; + } + + @Override + public Void visit(Offside symbol) { + + Symbol sym = symbol.getSymbol(); + + sym.setEnv(symbol.getEnv()); + visitSymbol(sym); + + symbol.setEnv(sym.getEnv()); + + return null; + } + + @Override + public Void visit(Terminal symbol) { + return null; + } + + @Override + public Void visit(While symbol) { + + org.iguana.datadependent.ast.Expression expression = symbol.getExpression(); + Symbol body = symbol.getBody(); + + expression.setEnv(symbol.getEnv()); + expression.accept(this); + + body.setEnv(symbol.getEnv()); + visitSymbol(body); + + return null; + } + + @Override + public Void visit(Return symbol) { + org.iguana.datadependent.ast.Expression expression = symbol.getExpression(); + + expression.setEnv(symbol.getEnv()); + expression.accept(this); + + symbol.setEnv(expression.getEnv()); + + return null; + } + + @Override + public Void visit(Alt symbol) { + io.usethesource.capsule.Set.Immutable env = symbol.getEnv(); + + for (Symbol sym : symbol.getSymbols()) { + sym.setEnv(env); + visitSymbol(sym); + } + + return null; + } + + @Override + public Void visit(Opt symbol) { + + Symbol sym = symbol.getSymbol(); + + sym.setEnv(symbol.getEnv()); + visitSymbol(sym); + + return null; + } + + @Override + public Void visit(Plus symbol) { + + Symbol sym = symbol.getSymbol(); + + sym.setEnv(symbol.getEnv()); + visitSymbol(sym); + + return null; + } + + @Override + public Void visit(Group symbol) { + io.usethesource.capsule.Set.Immutable env = symbol.getEnv(); + + for (Symbol sym : symbol.getSymbols()) { + sym.setEnv(env); + visitSymbol(sym); + env = sym.getEnv(); + } + + return null; + } + + @Override + public Void visit(Star symbol) { + + Symbol sym = symbol.getSymbol(); + + sym.setEnv(symbol.getEnv()); + visitSymbol(sym); + + return null; + } + + @Override + public Void visit(Start start) { +// Symbol sym = start.getNonterminal(); +// +// sym.setEnv(start.getEnv()); +// visitSymbol(sym); +// + return null; + } + + // Accounts for optional label and optional preconditions and postconditions + public Void visitSymbol(Symbol symbol) { + + io.usethesource.capsule.Set.Immutable env = symbol.getEnv(); + + if (symbol.getLabel() != null) + env = env.__insert(java.lang.String.format(LeftExtent.format, symbol.getLabel())); + + for (Condition condition : symbol.getPreConditions()) { + condition.setEnv(env); + condition.accept(this); + } + + symbol.setEnv(env); + symbol.accept(this); + + env = symbol.getEnv(); + + if (symbol.getLabel() != null) + env = env.__insert(symbol.getLabel()); + + for (Condition condition : symbol.getPostConditions()) { + condition.setEnv(env); + condition.accept(this); + } + + symbol.setEnv(env); + + return null; + } + + @Override + public Void visit(DataDependentCondition condition) { + + org.iguana.datadependent.ast.Expression expression = condition.getExpression(); + + expression.setEnv(condition.getEnv()); + expression.accept(this); + + return null; + } + + @Override + public Void visit(PositionalCondition condition) { + return null; + } + + @Override + public Void visit(RegularExpressionCondition condition) { + return null; + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/datadependent/traversal/IAbstractASTVisitor.java b/benchmarks/src/main/kotlin/org/iguana/datadependent/traversal/IAbstractASTVisitor.java new file mode 100644 index 000000000..c82d9060e --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/datadependent/traversal/IAbstractASTVisitor.java @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.datadependent.traversal; + +import org.iguana.datadependent.ast.AST; +import org.iguana.datadependent.ast.Expression.Add; +import org.iguana.datadependent.ast.Expression.And; +import org.iguana.datadependent.ast.Expression.AndIndent; +import org.iguana.datadependent.ast.Expression.Assignment; +import org.iguana.datadependent.ast.Expression.Boolean; +import org.iguana.datadependent.ast.Expression.Call; +import org.iguana.datadependent.ast.Expression.Divide; +import org.iguana.datadependent.ast.Expression.EndOfFile; +import org.iguana.datadependent.ast.Expression.Equal; +import org.iguana.datadependent.ast.Expression.Greater; +import org.iguana.datadependent.ast.Expression.GreaterThanEqual; +import org.iguana.datadependent.ast.Expression.IfThenElse; +import org.iguana.datadependent.ast.Expression.IntTuple2; +import org.iguana.datadependent.ast.Expression.Integer; +import org.iguana.datadependent.ast.Expression.LShiftANDEqZero; +import org.iguana.datadependent.ast.Expression.LeftExtent; +import org.iguana.datadependent.ast.Expression.Less; +import org.iguana.datadependent.ast.Expression.LessThanEqual; +import org.iguana.datadependent.ast.Expression.Multiply; +import org.iguana.datadependent.ast.Expression.Name; +import org.iguana.datadependent.ast.Expression.Not; +import org.iguana.datadependent.ast.Expression.NotEqual; +import org.iguana.datadependent.ast.Expression.Or; +import org.iguana.datadependent.ast.Expression.OrIndent; +import org.iguana.datadependent.ast.Expression.Real; +import org.iguana.datadependent.ast.Expression.RightExtent; +import org.iguana.datadependent.ast.Expression.String; +import org.iguana.datadependent.ast.Expression.Subtract; +import org.iguana.datadependent.ast.Expression.Tuple; +import org.iguana.datadependent.ast.Expression.Val; +import org.iguana.datadependent.ast.Expression.Yield; +import org.iguana.datadependent.ast.Statement; +import org.iguana.datadependent.ast.VariableDeclaration; + +public interface IAbstractASTVisitor { + + T visit(Boolean expression); + + T visit(Integer expression); + + T visit(Real expression); + + T visit(String expression); + + T visit(Not not); + + T visit(Tuple expression); + + default T visit(IntTuple2 expression) { + return AST.tuple(expression.getElement1(), expression.getElement2()).accept(this); + } + + T visit(Name expression); + + T visit(Call expression); + + T visit(Assignment expression); + + T visit(LShiftANDEqZero expression); + + T visit(OrIndent expression); + + T visit(AndIndent expression); + + T visit(Or expression); + + T visit(And expression); + + T visit(Less expression); + + T visit(LessThanEqual expression); + + T visit(Greater expression); + + T visit(GreaterThanEqual expression); + + T visit(Equal expression); + + T visit(NotEqual expression); + + T visit(Add expression); + + T visit(Subtract expression); + + T visit(Multiply expression); + + T visit(Divide expression); + + T visit(LeftExtent expression); + + T visit(RightExtent expression); + + T visit(Yield expression); + + T visit(Val expression); + + T visit(EndOfFile expression); + + T visit(IfThenElse expression); + + T visit(VariableDeclaration declaration); + + T visit(Statement.Expression statement); + + T visit(Statement.VariableDeclaration statement); + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/datadependent/traversal/ValUses.java b/benchmarks/src/main/kotlin/org/iguana/datadependent/traversal/ValUses.java new file mode 100644 index 000000000..e5507d589 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/datadependent/traversal/ValUses.java @@ -0,0 +1,415 @@ +package org.iguana.datadependent.traversal; + +import org.iguana.datadependent.ast.Expression.Add; +import org.iguana.datadependent.ast.Expression.And; +import org.iguana.datadependent.ast.Expression.AndIndent; +import org.iguana.datadependent.ast.Expression.Assignment; +import org.iguana.datadependent.ast.Expression.Boolean; +import org.iguana.datadependent.ast.Expression.Call; +import org.iguana.datadependent.ast.Expression.Divide; +import org.iguana.datadependent.ast.Expression.EndOfFile; +import org.iguana.datadependent.ast.Expression.Equal; +import org.iguana.datadependent.ast.Expression.Greater; +import org.iguana.datadependent.ast.Expression.GreaterThanEqual; +import org.iguana.datadependent.ast.Expression.Integer; +import org.iguana.datadependent.ast.Expression.LShiftANDEqZero; +import org.iguana.datadependent.ast.Expression.LeftExtent; +import org.iguana.datadependent.ast.Expression.Less; +import org.iguana.datadependent.ast.Expression.LessThanEqual; +import org.iguana.datadependent.ast.Expression.Multiply; +import org.iguana.datadependent.ast.Expression.Name; +import org.iguana.datadependent.ast.Expression.Not; +import org.iguana.datadependent.ast.Expression.NotEqual; +import org.iguana.datadependent.ast.Expression.Or; +import org.iguana.datadependent.ast.Expression.OrIndent; +import org.iguana.datadependent.ast.Expression.Real; +import org.iguana.datadependent.ast.Expression.RightExtent; +import org.iguana.datadependent.ast.Expression.String; +import org.iguana.datadependent.ast.Expression.Subtract; +import org.iguana.datadependent.ast.Expression.Tuple; +import org.iguana.datadependent.ast.Expression.Val; +import org.iguana.datadependent.ast.Expression.Yield; +import org.iguana.datadependent.ast.Statement; +import org.iguana.datadependent.ast.Statement.Expression; +import org.iguana.datadependent.ast.VariableDeclaration; +import org.iguana.grammar.condition.Condition; +import org.iguana.grammar.condition.DataDependentCondition; +import org.iguana.grammar.condition.PositionalCondition; +import org.iguana.grammar.condition.RegularExpressionCondition; +import org.iguana.grammar.exception.UnexpectedSymbolException; +import org.iguana.grammar.symbol.Align; +import org.iguana.grammar.symbol.Alt; +import org.iguana.grammar.symbol.Block; +import org.iguana.grammar.symbol.Code; +import org.iguana.grammar.symbol.Conditional; +import org.iguana.grammar.symbol.Error; +import org.iguana.grammar.symbol.Group; +import org.iguana.grammar.symbol.IfThen; +import org.iguana.grammar.symbol.IfThenElse; +import org.iguana.grammar.symbol.Ignore; +import org.iguana.grammar.symbol.Nonterminal; +import org.iguana.grammar.symbol.Offside; +import org.iguana.grammar.symbol.Opt; +import org.iguana.grammar.symbol.Plus; +import org.iguana.grammar.symbol.Return; +import org.iguana.grammar.symbol.Star; +import org.iguana.grammar.symbol.Start; +import org.iguana.grammar.symbol.Symbol; +import org.iguana.grammar.symbol.Terminal; +import org.iguana.grammar.symbol.While; +import org.iguana.traversal.IConditionVisitor; +import org.iguana.traversal.ISymbolVisitor; + +import java.util.HashSet; +import java.util.Set; + +public class ValUses implements IAbstractASTVisitor, ISymbolVisitor, IConditionVisitor { + + public final Set labels = new HashSet<>(); + + @Override + public Void visit(DataDependentCondition condition) { + condition.getExpression().accept(this); + return null; + } + + @Override + public Void visit(PositionalCondition condition) { + return null; + } + + @Override + public Void visit(RegularExpressionCondition condition) { + return null; + } + + @Override + public Void visit(Align symbol) { + throw new UnexpectedSymbolException(symbol, "val-uses traversal"); + } + + @Override + public Void visit(Block symbol) { + throw new UnexpectedSymbolException(symbol, "val-uses traversal"); + } + + @Override + public Void visit(Code symbol) { + visitSymbol(symbol.getSymbol()); + + for (Statement stat : symbol.getStatements()) + stat.accept(this); + + return null; + } + + @Override + public Void visit(Error error) { + return null; + } + + @Override + public Void visit(Conditional symbol) { + visitSymbol(symbol.getSymbol()); + symbol.getExpression().accept(this); + return null; + } + + @Override + public Void visit(IfThen symbol) { + throw new UnexpectedSymbolException(symbol, "val-uses traversal"); + } + + @Override + public Void visit(IfThenElse symbol) { + throw new UnexpectedSymbolException(symbol, "val-uses traversal"); + } + + @Override + public Void visit(Ignore symbol) { + throw new UnexpectedSymbolException(symbol, "val-uses traversal"); + } + + @Override + public Void visit(Nonterminal symbol) { + for (org.iguana.datadependent.ast.Expression arg : symbol.getArguments()) + arg.accept(this); + return null; + } + + @Override + public Void visit(Offside symbol) { + throw new UnexpectedSymbolException(symbol, "val-uses traversal"); + } + + @Override + public Void visit(Terminal symbol) { + return null; + } + + @Override + public Void visit(While symbol) { + throw new UnexpectedSymbolException(symbol, "val-uses traversal"); + } + + @Override + public Void visit(Return symbol) { + symbol.getExpression().accept(this); + return null; + } + + @Override + public Void visit(Alt symbol) { + throw new UnexpectedSymbolException(symbol, "val-uses traversal"); + } + + @Override + public Void visit(Opt symbol) { + throw new UnexpectedSymbolException(symbol, "val-uses traversal"); + } + + @Override + public Void visit(Plus symbol) { + throw new UnexpectedSymbolException(symbol, "val-uses traversal"); + } + + @Override + public Void visit(Group symbol) { + throw new UnexpectedSymbolException(symbol, "val-uses traversal"); + } + + @Override + public Void visit(Star symbol) { + throw new UnexpectedSymbolException(symbol, "val-uses traversal"); + } + + @Override + public Void visit(Start symbol) { + throw new UnexpectedSymbolException(symbol, "val-uses traversal"); + } + + private void visitSymbol(Symbol symbol) { + for (Condition cond : symbol.getPreConditions()) + cond.accept(this); + + symbol.accept(this); + + for (Condition cond : symbol.getPostConditions()) + cond.accept(this); + } + + @Override + public Void visit(Boolean expression) { + return null; + } + + @Override + public Void visit(Integer expression) { + return null; + } + + @Override + public Void visit(Real expression) { + return null; + } + + @Override + public Void visit(String expression) { + return null; + } + + @Override + public Void visit(Not not) { + not.getExp().accept(this); + return null; + } + + @Override + public Void visit(Tuple expression) { + for (org.iguana.datadependent.ast.Expression element : expression.getElements()) + element.accept(this); + + return null; + } + + @Override + public Void visit(Name expression) { + return null; + } + + @Override + public Void visit(Call expression) { + for (org.iguana.datadependent.ast.Expression arg : expression.getArguments()) + arg.accept(this); + + return null; + } + + @Override + public Void visit(Assignment expression) { + expression.getExpression().accept(this); + return null; + } + + @Override + public Void visit(LShiftANDEqZero expression) { + expression.getLhs().accept(this); + expression.getRhs().accept(this); + return null; + } + + @Override + public Void visit(OrIndent expression) { + expression.getFirst().accept(this); + expression.getIndent().accept(this); + expression.getIndex().accept(this); + expression.getLExt().accept(this); + return null; + } + + @Override + public Void visit(AndIndent expression) { + expression.getFirst().accept(this); + expression.getIndex().accept(this); + expression.getLExt().accept(this); + return null; + } + + @Override + public Void visit(Or expression) { + expression.getLhs().accept(this); + expression.getRhs().accept(this); + return null; + } + + @Override + public Void visit(And expression) { + expression.getLhs().accept(this); + expression.getRhs().accept(this); + return null; + } + + @Override + public Void visit(Less expression) { + expression.getLhs().accept(this); + expression.getRhs().accept(this); + return null; + } + + @Override + public Void visit(LessThanEqual expression) { + expression.getLhs().accept(this); + expression.getRhs().accept(this); + return null; + } + + @Override + public Void visit(Greater expression) { + expression.getLhs().accept(this); + expression.getRhs().accept(this); + return null; + } + + @Override + public Void visit(GreaterThanEqual expression) { + expression.getLhs().accept(this); + expression.getRhs().accept(this); + return null; + } + + @Override + public Void visit(Equal expression) { + expression.getLhs().accept(this); + expression.getRhs().accept(this); + return null; + } + + @Override + public Void visit(NotEqual expression) { + expression.getLhs().accept(this); + expression.getRhs().accept(this); + return null; + } + + @Override + public Void visit(LeftExtent expression) { + return null; + } + + @Override + public Void visit(RightExtent expression) { + return null; + } + + @Override + public Void visit(EndOfFile expression) { + return null; + } + + @Override + public Void visit(org.iguana.datadependent.ast.Expression.IfThenElse expression) { + expression.getCondition().accept(this); + expression.getThenPart().accept(this); + expression.getElsePart().accept(this); + return null; + } + + @Override + public Void visit(Yield expression) { + return null; + } + + @Override + public Void visit(Val expression) { + labels.add(expression.getLabel()); + return null; + } + + @Override + public Void visit(VariableDeclaration declaration) { + if (declaration.getExpression() != null) + declaration.getExpression().accept(this); + return null; + } + + @Override + public Void visit(org.iguana.datadependent.ast.Statement.VariableDeclaration declaration) { + declaration.getDeclaration().accept(this); + return null; + } + + @Override + public Void visit(Add expression) { + expression.getLhs().accept(this); + expression.getRhs().accept(this); + return null; + } + + @Override + public Void visit(Subtract expression) { + expression.getLhs().accept(this); + expression.getRhs().accept(this); + return null; + } + + @Override + public Void visit(Multiply expression) { + expression.getLhs().accept(this); + expression.getRhs().accept(this); + return null; + } + + @Override + public Void visit(Divide expression) { + expression.getLhs().accept(this); + expression.getRhs().accept(this); + return null; + } + + @Override + public Void visit(Expression statement) { + statement.getExpression().accept(this); + return null; + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/datadependent/values/Stack.java b/benchmarks/src/main/kotlin/org/iguana/datadependent/values/Stack.java new file mode 100644 index 000000000..6e0d4c2e5 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/datadependent/values/Stack.java @@ -0,0 +1,87 @@ +package org.iguana.datadependent.values; + +import org.iguana.utils.collections.hash.MurmurHash3; + +public class Stack { + + private static MurmurHash3 f = new MurmurHash3(); + + private final Stack parent; + private final T top; + + private int size; + private int hash; + + Stack(T top) { + this.parent = null; + this.top = top; + this.size = 1; + this.hash = top.hashCode(); + } + + Stack(T top, Stack parent) { + this.parent = parent; + this.top = top; + this.size = parent.size + 1; + + int[] hs = new int[this.size]; + Stack root = this; + int i = 0; + while (root != null) { + hs[i++] = root.top.hashCode(); + root = root.parent; + } + + this.hash = MurmurHash3.fn().apply(hs); + } + + public static Stack from(T top) { + return new Stack<>(top); + } + + public T top() { + return this.top; + } + + public Stack push(T top) { + return new Stack<>(top, this); + } + + public Stack pop() { + return parent; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + + if (!(obj instanceof Stack)) + return false; + + Stack other = (Stack) obj; + + if (top.equals(other.top)) { + if (parent == null) + return other.parent == null; + else + return parent.equals(other.parent); + + } + + return false; + } + + @Override + public int hashCode() { + return hash; + } + + @Override + public String toString() { + if (parent == null) + return top.toString(); + + return top.toString() + "," + parent.toString(); + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/generator/Generator.java b/benchmarks/src/main/kotlin/org/iguana/generator/Generator.java new file mode 100644 index 000000000..441795856 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/generator/Generator.java @@ -0,0 +1,64 @@ +package org.iguana.generator; + +import org.iguana.grammar.runtime.RuntimeGrammar; +import org.iguana.grammar.runtime.RuntimeRule; +import org.iguana.grammar.symbol.Code; +import org.iguana.grammar.symbol.Nonterminal; +import org.iguana.grammar.symbol.Symbol; + +import java.util.List; +import java.util.Map; +import java.util.function.Function; + +public abstract class Generator { + + protected final RuntimeGrammar grammar; + protected final String grammarName; + protected final String packageName; + protected final String genDirectory; + + public Generator(RuntimeGrammar grammar, String grammarName, String packageName, String genDirectory) { + this.grammar = grammar; + this.grammarName = grammarName; + this.packageName = packageName; + this.genDirectory = genDirectory; + } + + protected String symbolToString(Symbol symbol) { + String label = getLabel(symbol); + return (label == null ? "" : label + ":") + symbol.getName(); + } + + protected String getLabel(Symbol symbol) { + if (symbol instanceof Code) { + if (symbol.getLabel() != null) return symbol.getLabel(); + return ((Code) symbol).getSymbol().getLabel(); + } + return symbol.getLabel(); + } + + /** + * + * @param f a function from the nonterminal name to the generated string + */ + protected void generateTypes(StringBuilder sb, Function f) { + for (Map.Entry> entry : grammar.getDefinitions().entrySet()) { + String nonterminalName = entry.getKey().getName(); + List alternatives = entry.getValue(); + + if (alternatives.size() == 0) { + sb.append(f.apply(nonterminalName)); + } else if (alternatives.size() == 1) { + sb.append(f.apply(nonterminalName)); + } else { + for (RuntimeRule alternative : alternatives) { + if (alternative.getLabel() == null) + throw new RuntimeException("All alternatives must have a label: " + alternative); + String nodeName = alternative.getLabel() + nonterminalName.substring(0, 1).toUpperCase() + + nonterminalName.substring(1); + sb.append(f.apply(nodeName)); + } + } + } + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/generator/GeneratorUtils.java b/benchmarks/src/main/kotlin/org/iguana/generator/GeneratorUtils.java new file mode 100644 index 000000000..67446ad4b --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/generator/GeneratorUtils.java @@ -0,0 +1,24 @@ +package org.iguana.generator; + +import org.iguana.utils.io.FileUtils; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; + +public class GeneratorUtils { + + public static void writeToJavaFile(String content, String genDirectory, String className) { + writeToFile(content, genDirectory, className, "java"); + } + + public static void writeToFile(String content, String genDirectory, String className, String extension) { + try { + Files.createDirectories(Paths.get(genDirectory)); + FileUtils.writeFile(content, new File(genDirectory, className + "." + extension).getAbsolutePath()); + } catch (IOException e) { + throw new RuntimeException(e); + } + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/generator/ide/GenerateLangFiles.java b/benchmarks/src/main/kotlin/org/iguana/generator/ide/GenerateLangFiles.java new file mode 100644 index 000000000..531c8a47c --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/generator/ide/GenerateLangFiles.java @@ -0,0 +1,108 @@ +package org.iguana.generator.ide; + +import java.io.File; + +import static org.iguana.generator.GeneratorUtils.writeToJavaFile; +import static org.iguana.utils.string.StringUtil.toFirstUpperCase; + +public class GenerateLangFiles { + + private final String grammarName; + private final String langGenDirectory; + + public GenerateLangFiles(String grammarName, String genDirectory) { + this.grammarName = grammarName; + this.langGenDirectory = new File(genDirectory, "lang").getAbsolutePath(); + } + + public void generate() { + generateFileType(); + generateIcon(); + generateLang(); + } + + private void generateFileType() { + String className = toFirstUpperCase(grammarName); + String content = + "// This file has been generated, do not directly edit this file!\n" + + "\n" + + "package " + grammarName.toLowerCase() + ".ide.lang;\n" + + "\n" + + "import com.intellij.openapi.fileTypes.LanguageFileType;\n" + + "import com.intellij.openapi.util.NlsContexts;\n" + + "import com.intellij.openapi.util.NlsSafe;\n" + + "import org.jetbrains.annotations.NonNls;\n" + + "import org.jetbrains.annotations.NotNull;\n" + + "\n" + + "import javax.swing.*;\n" + + "\n" + + "public class " + className + "FileType extends LanguageFileType {\n" + + "\n" + + "public static final " + className + "FileType instance = new " + className + "FileType();\n" + + "\n" + + " protected " + className + "FileType() {\n" + + " super(" + className + "Lang.instance);\n" + + " }\n" + + "\n" + + " @Override\n" + + " public @NonNls @NotNull String getName() {\n" + + " return \"" + className + "\";\n" + + " }\n" + + "\n" + + " public @NlsContexts.Label @NotNull String getDescription() {\n" + + " return \"" + className + " grammar definition file\"\n;" + + " }\n" + + "\n" + + " @Override\n" + + " public @NlsSafe @NotNull String getDefaultExtension() {\n" + + " return \"" + className + "\";\n" + + " }\n" + + "\n" + + " @Override\n" + + " public Icon getIcon() {\n" + + " return " + className + "Icon.icon;\n" + + " }\n" + + "}\n"; + + writeToJavaFile(content, langGenDirectory, className + "FileType"); + } + + private void generateIcon() { + String className = toFirstUpperCase(grammarName); + String content = + "// This file has been generated, do not directly edit this file!\n" + + "\n" + + "package " + grammarName.toLowerCase() + ".ide.lang;\n" + + "\n" + + "import com.intellij.openapi.util.IconLoader;\n" + + "\n" + + "import javax.swing.*;\n" + + "\n" + + "public class " + className + "Icon {\n" + + " public static final Icon icon = IconLoader.getIcon(\"/META-INF/pluginIcon.svg\", " + className + + "Icon.class);\n" + + "}\n"; + writeToJavaFile(content, langGenDirectory, className + "Icon"); + } + + private void generateLang() { + String className = toFirstUpperCase(grammarName); + String content = + "// This file has been generated, do not directly edit this file!\n" + + "\n" + + "package " + grammarName.toLowerCase() + ".ide.lang;\n" + + "\n" + + "import com.intellij.lang.Language;\n" + + "\n" + + "public class " + className + "Lang extends Language {\n" + + "\n" + + " public static final " + className + "Lang instance = new " + className + "Lang();\n" + + "\n" + + " private " + className + "Lang() {\n" + + " super(\"" + className + "\");\n" + + " }\n" + + "}\n" + + "\n"; + writeToJavaFile(content, langGenDirectory, className + "Lang"); + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/generator/ide/GenerateParserFiles.java b/benchmarks/src/main/kotlin/org/iguana/generator/ide/GenerateParserFiles.java new file mode 100644 index 000000000..2a27fbac7 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/generator/ide/GenerateParserFiles.java @@ -0,0 +1,70 @@ +package org.iguana.generator.ide; + +import org.iguana.generator.Generator; +import org.iguana.grammar.runtime.RuntimeGrammar; + +import java.io.File; +import java.util.Arrays; +import java.util.List; + +import static org.iguana.generator.GeneratorUtils.writeToJavaFile; +import static org.iguana.utils.string.StringUtil.toFirstUpperCase; + +public class GenerateParserFiles extends Generator { + + private final String parserGenDirectory; + private final String className; + private final List metaSymbolNodes = Arrays.asList("Opt", "Star", "Plus", "Group", "Alt", "Start"); + + public GenerateParserFiles(RuntimeGrammar grammar, String grammarName, String packageName, String genDirectory) { + super(grammar, grammarName, packageName, genDirectory); + this.parserGenDirectory = new File(genDirectory, "parser").getAbsolutePath(); + this.className = toFirstUpperCase(grammarName); + } + + public void generate() { + generateIggyElementTypes(); + } + + private void generateIggyElementTypes() { + StringBuilder sb = new StringBuilder(); + sb.append("// This file has been generated, do not directly edit this file!\n"); + sb.append("\n"); + sb.append("package " + grammarName.toLowerCase() + ".ide.parser;\n"); + sb.append("\n"); + sb.append("import com.intellij.psi.tree.IElementType;\n"); + sb.append("import iggy.ide.lang.IggyLang;\n"); + sb.append("\n"); + sb.append("import java.util.HashMap;\n"); + sb.append("import java.util.Map;\n"); + sb.append("\n"); + sb.append("public class " + className + "ElementTypes {\n"); + metaSymbolNodes.forEach(s -> sb.append(generateElementType(s))); + sb.append("\n"); + generateTypes(sb, this::generateElementType); + sb.append("\n"); + sb.append(" private static final Map stringToElementTypeMap = new HashMap<>();\n"); + sb.append("\n"); + sb.append(" static {\n"); + metaSymbolNodes.forEach(s -> sb.append(generateStringToElementTypetMapEntry(s))); + sb.append("\n"); + generateTypes(sb, this::generateStringToElementTypetMapEntry); + sb.append(" }\n"); + sb.append("\n"); + sb.append(" public static IElementType getElementType(String name) {\n"); + sb.append(" return stringToElementTypeMap.get(name);\n"); + sb.append(" }\n"); + sb.append("}\n"); + + writeToJavaFile(sb.toString(), parserGenDirectory, className + "ElementTypes"); + } + + private String generateElementType(String type) { + return " public static IElementType " + type + " = new IggyElementType(\"" + type + + "\", IggyLang.instance);\n"; + } + + private String generateStringToElementTypetMapEntry(String type) { + return " stringToElementTypeMap.put(\"" + type + "\"" + ", " + type + ");\n"; + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/generator/ide/GeneratePsiElements.java b/benchmarks/src/main/kotlin/org/iguana/generator/ide/GeneratePsiElements.java new file mode 100644 index 000000000..c3761490c --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/generator/ide/GeneratePsiElements.java @@ -0,0 +1,209 @@ +package org.iguana.generator.ide; + +import org.iguana.generator.Generator; +import org.iguana.grammar.runtime.RuntimeGrammar; +import org.iguana.grammar.runtime.RuntimeRule; +import org.iguana.grammar.symbol.Alt; +import org.iguana.grammar.symbol.Code; +import org.iguana.grammar.symbol.Group; +import org.iguana.grammar.symbol.Nonterminal; +import org.iguana.grammar.symbol.Opt; +import org.iguana.grammar.symbol.Plus; +import org.iguana.grammar.symbol.Star; +import org.iguana.grammar.symbol.Start; +import org.iguana.grammar.symbol.Symbol; +import org.iguana.grammar.symbol.Terminal; + +import java.io.File; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import static org.iguana.generator.GeneratorUtils.writeToJavaFile; +import static org.iguana.utils.string.StringUtil.toFirstUpperCase; + +public class GeneratePsiElements extends Generator { + + private final List metaSymbolNodes = Arrays.asList("Opt", "Star", "Plus", "Group", "Alt", "Start"); + private final String psiGenDirectory; + private final String className; + + public GeneratePsiElements(RuntimeGrammar grammar, String grammarName, String packageName, String genDirectory) { + super(grammar, grammarName, packageName, genDirectory); + this.psiGenDirectory = new File(genDirectory, "psi").getAbsolutePath(); + this.className = toFirstUpperCase(grammarName); + } + + public void generate() { + generatePsiElements(); + generatePsiElementFactory(); + } + + private void generatePsiElements() { + StringBuilder sb = new StringBuilder(); + sb.append("// This file has been generated, do not directly edit this file!\n"); + sb.append("\n"); + sb.append("package " + grammarName.toLowerCase() + ".ide.psi;\n"); + sb.append("\n"); + sb.append("import com.intellij.extapi.psi.ASTWrapperPsiElement;\n"); + sb.append("import com.intellij.lang.ASTNode;\n"); + sb.append("import com.intellij.psi.PsiElement;\n"); + sb.append("import org.jetbrains.annotations.NotNull;\n"); + sb.append("\n"); + sb.append("import java.util.List;\n"); + sb.append("import java.util.Optional;\n"); + sb.append("\n"); + String className = toFirstUpperCase(grammarName) + "PsiElement"; + sb.append("public class " + className + " {\n"); + sb.append("\n"); + + metaSymbolNodes.forEach(node -> + sb.append(generateSymbolClass(node, false, Collections.emptyList()))); + sb.append("\n"); + + for (Map.Entry> entry : grammar.getDefinitions().entrySet()) { + String nonterminalName = entry.getKey().getName(); + List alternatives = entry.getValue(); + + if (alternatives.size() == 0) { + sb.append(generateSymbolClass(nonterminalName, false, Collections.emptyList())); + } else if (alternatives.size() == 1) { + sb.append(generateSymbolClass(nonterminalName, false, alternatives.get(0).getBody())); + } else { + sb.append(generateSymbolClass(nonterminalName, true, Collections.emptyList())); + for (RuntimeRule alternative : alternatives) { + if (alternative.getLabel() == null) + throw new RuntimeException("All alternatives must have a label: " + alternative); + String nodeName = alternative.getLabel() + nonterminalName.substring(0, 1).toUpperCase() + + nonterminalName.substring(1); + sb.append(generateSymbolClass(nodeName, false, alternative.getBody())); + } + } + } + sb.append("}\n"); + writeToJavaFile(sb.toString(), psiGenDirectory, className); + System.out.println(className + " has been generated."); + } + + // CHECKSTYLE:OFF LineLength + private void generatePsiElementFactory() { + StringBuilder sb = new StringBuilder(); + sb.append("// This file has been generated, do not directly edit this file!\n"); + sb.append("\n"); + sb.append("package " + grammarName.toLowerCase() + ".ide.psi;\n"); + sb.append("\n"); + sb.append("import com.intellij.lang.ASTNode;\n"); + sb.append("import com.intellij.psi.PsiElement;\n"); + sb.append("import com.intellij.psi.tree.IElementType;\n"); + sb.append("\n"); + sb.append("import java.util.IdentityHashMap;\n"); + sb.append("import java.util.Map;\n"); + sb.append("import java.util.function.Function;\n"); + sb.append("\n"); + sb.append("import static iggy.ide.parser.IggyElementTypes.*;\n"); + sb.append("\n"); + sb.append("public class " + className + "PsiElementFactory {\n"); + sb.append("\n"); + sb.append(" private static final Map> elementTypeToPsiElementMap = new IdentityHashMap<>();\n"); + sb.append("\n"); + sb.append(" static {\n"); + metaSymbolNodes.forEach(s -> sb.append(generateTypeToPsiElementMapEntry(s))); + sb.append("\n"); + generateTypes(sb, this::generateTypeToPsiElementMapEntry); + sb.append("\n"); + sb.append(" }\n"); + sb.append(" public static PsiElement createPsiElement(ASTNode node) {\n"); + sb.append(" return elementTypeToPsiElementMap.get(node.getElementType()).apply(node);\n"); + sb.append(" }\n"); + sb.append("}\n"); + + writeToJavaFile(sb.toString(), psiGenDirectory, className + "PsiElementFactory"); + } + // CHECKSTYLE:ON LineLength + + private String generateTypeToPsiElementMapEntry(String type) { + return " elementTypeToPsiElementMap.put(" + type + ", node -> new IggyPsiElement." + type + "(node));\n"; + } + + private String generateSymbolClass(String symbolClass, boolean isAbstract, List symbols) { + return + " public static " + (isAbstract ? "abstract " : "") + "class " + symbolClass + + " extends ASTWrapperPsiElement {\n" + + " public " + symbolClass + "(@NotNull ASTNode node) {\n" + + " super(node);\n" + + " }\n\n" + + generateSymbols(symbols) + + " }\n\n"; + } + + private String generateSymbols(List symbols) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < symbols.size(); i++) { + Symbol symbol = symbols.get(i); + Type type = getSymbolType(symbol); + // When type == null, it's a data-dependent construct that does not contribute to parse trees + if (type != null) { + String label = getLabel(symbol); + if (label != null) { + sb.append(" public " + type + " " + label + "() {\n"); + switch (type.name) { + case "Optional": + sb.append(" return Optional.of(getChildren()[" + i + "]);\n"); + break; + default: + sb.append(" return (" + type + ") getChildren()[" + i + "];\n"); + break; + } + sb.append(" }\n"); + } + } + } + return sb.toString(); + } + + private static class Type { + private final String name; + private final String parameter; + + Type(String name) { + this(name, null); + } + + Type(String name, String parameter) { + this.name = name; + this.parameter = parameter; + } + + @Override + public String toString() { + if (parameter == null) return name; + else return String.format("%s<%s>", name, parameter); + } + } + + private Type getSymbolType(Symbol symbol) { + if (symbol instanceof Nonterminal) { + return new Type(toFirstUpperCase(grammarName) + "PsiElement." + symbol.getName()); + } else if (symbol instanceof Terminal) { + return new Type("PsiElement"); + } else if (symbol instanceof Star) { + return new Type("List", "PsiElement"); + } else if (symbol instanceof Plus) { + return new Type("List", "PsiElement"); + } else if (symbol instanceof Group) { + return new Type("List", "PsiElement"); + } else if (symbol instanceof Alt) { + return new Type("PsiElement"); + } else if (symbol instanceof Opt) { + return new Type("Optional", "PsiElement"); + } else if (symbol instanceof Start) { + return new Type("PsiElement"); + } else if (symbol instanceof Code) { + return getSymbolType(((Code) symbol).getSymbol()); + } else { + // Data dependent symbols do not have a type + return null; + } + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/generator/parser/ParseTreeVisitorGenerator.java b/benchmarks/src/main/kotlin/org/iguana/generator/parser/ParseTreeVisitorGenerator.java new file mode 100644 index 000000000..4ce33fd57 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/generator/parser/ParseTreeVisitorGenerator.java @@ -0,0 +1,294 @@ +package org.iguana.generator.parser; + +import org.iguana.generator.Generator; +import org.iguana.grammar.runtime.RuntimeGrammar; +import org.iguana.grammar.runtime.RuntimeRule; +import org.iguana.grammar.symbol.Alt; +import org.iguana.grammar.symbol.Code; +import org.iguana.grammar.symbol.Group; +import org.iguana.grammar.symbol.Nonterminal; +import org.iguana.grammar.symbol.Opt; +import org.iguana.grammar.symbol.Plus; +import org.iguana.grammar.symbol.Star; +import org.iguana.grammar.symbol.Start; +import org.iguana.grammar.symbol.Symbol; +import org.iguana.grammar.symbol.Terminal; +import org.iguana.parsetree.NonterminalNode; +import org.iguana.parsetree.TerminalNode; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import static org.iguana.generator.GeneratorUtils.writeToJavaFile; +import static org.iguana.parsetree.MetaSymbolNode.AltNode; +import static org.iguana.parsetree.MetaSymbolNode.GroupNode; +import static org.iguana.parsetree.MetaSymbolNode.OptionNode; +import static org.iguana.parsetree.MetaSymbolNode.PlusNode; +import static org.iguana.parsetree.MetaSymbolNode.StarNode; +import static org.iguana.parsetree.MetaSymbolNode.StartNode; +import static org.iguana.utils.string.StringUtil.listToString; +import static org.iguana.utils.string.StringUtil.toFirstUpperCase; + +public class ParseTreeVisitorGenerator extends Generator { + + public ParseTreeVisitorGenerator( + RuntimeGrammar grammar, + String grammarName, + String packageName, + String genDirectory) { + super(grammar, grammarName, packageName, genDirectory); + } + + public void generate() { + generateParseTreeTypes(); + generateVisitor(); + generateParseTreeBuilder(); + } + + private void generateParseTreeBuilder() { + StringBuilder sb = new StringBuilder(); + sb.append("// This file has been generated, do not directly edit this file!\n"); + sb.append("\n"); + sb.append("package " + packageName + ";\n"); + sb.append("\n"); + sb.append("import org.iguana.grammar.runtime.RuntimeRule;\n"); + sb.append("import org.iguana.parsetree.DefaultParseTreeBuilder;\n"); + sb.append("import org.iguana.parsetree.NonterminalNode;\n"); + sb.append("import org.iguana.parsetree.ParseTreeNode;\n"); + sb.append("import org.iguana.utils.input.Input;\n"); + sb.append("\n"); + sb.append("import java.util.List;\n\n"); + + String className = toFirstUpperCase(grammarName) + "ParseTreeBuilder"; + sb.append("public class " + className + " extends DefaultParseTreeBuilder {\n\n"); + sb.append(" public " + className + "(Input input) {\n"); + sb.append(" super(input);\n"); + sb.append(" }\n\n"); + + sb.append(" @Override\n"); + sb.append(" public NonterminalNode nonterminalNode(\n"); + sb.append(" RuntimeRule rule,\n"); + sb.append(" List children,\n"); + sb.append(" int leftExtent,\n"); + sb.append(" int rightExtent) {\n"); + sb.append(" java.lang.String name = rule.getHead().getName();\n"); + sb.append(" java.lang.String label = rule.getLabel();\n\n"); + sb.append(" switch (name) {\n"); + generateBuilderCases(grammar, sb); + sb.append(" default:\n"); + sb.append(" throw new RuntimeException(\"Unexpected nonterminal:\" + name);\n"); + sb.append(" }\n"); + sb.append(" }\n"); + sb.append("}\n"); + + writeToJavaFile(sb.toString(), genDirectory, className); + System.out.println(className + " has been generated in " + genDirectory + "/" + className + ".java"); + } + + private void generateBuilderCases(RuntimeGrammar grammar, StringBuilder sb) { + for (Map.Entry> entry : grammar.getDefinitions().entrySet()) { + String nonterminalName = entry.getKey().getName(); + List alternatives = entry.getValue(); + + sb.append(" case \"" + nonterminalName + "\":\n"); + if (alternatives.size() == 0) { + sb.append(" return new " + toFirstUpperCase(grammarName) + "ParseTree" + "." + + toFirstUpperCase(nonterminalName) + "(rule, children, leftExtent, rightExtent);\n"); + } else if (alternatives.size() == 1) { + sb.append(" return new " + toFirstUpperCase(grammarName) + "ParseTree" + "." + + toFirstUpperCase(nonterminalName) + "(rule, children, leftExtent, rightExtent);\n"); + } else { + sb.append(" switch (label) {\n"); + for (RuntimeRule alternative : alternatives) { + if (alternative.getLabel() == null) + throw new RuntimeException("All alternatives must have a label: " + alternative); + sb.append(" case \"" + alternative.getLabel() + "\":\n"); + String className = toFirstUpperCase(grammarName) + "ParseTree" + "." + + toFirstUpperCase(alternative.getLabel()) + toFirstUpperCase(nonterminalName); + sb.append(" return new " + toFirstUpperCase(className) + + "(rule, children, leftExtent, rightExtent);\n"); + } + sb.append(" default:\n"); + sb.append(" throw new RuntimeException(\"Unexpected label:\" + label);\n"); + sb.append(" }\n"); + } + } + } + + private void generateParseTreeTypes() { + StringBuilder sb = new StringBuilder(); + sb.append("// This file has been generated, do not directly edit this file!\n"); + sb.append("\n"); + sb.append("package " + packageName + ";\n\n"); + sb.append("import org.iguana.grammar.runtime.RuntimeRule;\n"); + sb.append("import org.iguana.parsetree.NonterminalNode;\n"); + sb.append("import org.iguana.parsetree.ParseTreeNode;\n"); + sb.append("import org.iguana.parsetree.ParseTreeVisitor;\n"); + sb.append("import org.iguana.parsetree.TerminalNode;\n"); + sb.append("\n"); + sb.append("import java.util.List;\n"); + sb.append("\n"); + sb.append("import static org.iguana.parsetree.MetaSymbolNode.OptionNode;\n"); + sb.append("import static org.iguana.parsetree.MetaSymbolNode.PlusNode;\n"); + sb.append("import static org.iguana.parsetree.MetaSymbolNode.StarNode;\n"); + sb.append("\n"); + String className = toFirstUpperCase(grammarName) + "ParseTree"; + sb.append("public class " + className + " {\n"); + for (Map.Entry> entry : grammar.getDefinitions().entrySet()) { + String nonterminalName = entry.getKey().getName(); + List alternatives = entry.getValue(); + + if (alternatives.size() == 0) { + sb.append(" // " + nonterminalName + " = \n"); + sb.append(generateSymbolClass(nonterminalName, NonterminalNode.class.getSimpleName(), false, + Collections.emptyList())); + } else if (alternatives.size() == 1) { + sb.append(" // " + nonterminalName + " = " + listToString(alternatives.get(0).getBody().stream() + .map(this::symbolToString).collect(Collectors.toList())) + "\n"); + sb.append(generateSymbolClass(nonterminalName, NonterminalNode.class.getSimpleName(), false, + alternatives.get(0).getBody())); + } else { + sb.append(generateSymbolClass(nonterminalName, NonterminalNode.class.getSimpleName(), true, + Collections.emptyList())); + for (RuntimeRule alternative : alternatives) { + if (alternative.getLabel() == null) + throw new RuntimeException("All alternatives must have a label: " + alternative); + String nodeName = alternative.getLabel() + nonterminalName.substring(0, 1).toUpperCase() + + nonterminalName.substring(1); + sb.append(" // " + nonterminalName + " = " + listToString(alternative.getBody().stream() + .map(this::symbolToString).collect(Collectors.toList())) + "\n"); + sb.append(generateSymbolClass(nodeName, nonterminalName, false, alternative.getBody())); + } + } + } + sb.append("}\n"); + writeToJavaFile(sb.toString(), genDirectory, className); + System.out.println(className + " has been generated in " + genDirectory + "/" + className + ".java"); + } + + private void generateVisitor() { + StringBuilder sb = new StringBuilder(); + sb.append("// This file has been generated, do not directly edit this file!\n"); + sb.append("\n"); + sb.append("package " + packageName + ";\n\n"); + sb.append("import org.iguana.parsetree.NonterminalNode;\n"); + sb.append("import org.iguana.parsetree.ParseTreeVisitor;\n\n"); + + String className = toFirstUpperCase(grammarName) + "ParseTreeVisitor"; + sb.append("public interface " + className + " extends ParseTreeVisitor {\n\n"); + sb.append(" @Override\n"); + sb.append(" default T visitNonterminalNode(NonterminalNode node) {\n"); + sb.append(" throw new UnsupportedOperationException();\n"); + sb.append(" }\n\n"); + generateVisitMethods(grammar, sb); + sb.append("}\n"); + writeToJavaFile(sb.toString(), genDirectory, className); + System.out.println(className + " has been generated in " + genDirectory + "/" + className + ".java"); + } + + private void generateVisitMethods(RuntimeGrammar grammar, StringBuilder sb) { + for (Map.Entry> entry : grammar.getDefinitions().entrySet()) { + String nonterminalName = entry.getKey().getName(); + List alternatives = entry.getValue(); + + if (alternatives.size() == 0) { + sb.append(generateVisitorMethod(nonterminalName)); + } else if (alternatives.size() == 1) { + sb.append(generateVisitorMethod(nonterminalName)); + } else { + for (RuntimeRule alternative : alternatives) { + if (alternative.getLabel() == null) + throw new RuntimeException("All alternatives must have a label: " + alternative); + String nodeName = alternative.getLabel() + nonterminalName.substring(0, 1).toUpperCase() + + nonterminalName.substring(1); + sb.append(generateVisitorMethod(nodeName)); + } + } + } + } + + private String generateVisitorMethod(String name) { + String className = toFirstUpperCase(grammarName) + "ParseTree"; + // CHECKSTYLE:OFF LineLength + if (name.startsWith("$_")) { + return " default T visit" + toFirstUpperCase(name) + "(" + className + "." + toFirstUpperCase(name) + " node) {\n" + + " return node.child().accept(this);\n" + + " }\n\n"; + } else { + return " T visit" + toFirstUpperCase(name) + "(" + className + "." + toFirstUpperCase(name) + " node);\n\n"; + } + // CHECKSTYLE:ON LineLength + } + + private String generateSymbolClass(String symbolClass, String superType, boolean isAbstract, List symbols) { + // CHECKSTYLE:OFF LineLength + return + " public " + (isAbstract ? "abstract " : "") + "static class " + toFirstUpperCase(symbolClass) + " extends " + toFirstUpperCase(superType) + " {\n" + + " public " + toFirstUpperCase(symbolClass) + "(RuntimeRule rule, List children, int start, int end) {\n" + + " super(rule, children, start, end);\n" + + " }\n\n" + + generateSymbols(symbols) + + (isAbstract ? "" : generateAcceptMethod(symbolClass)) + + " }\n\n"; + // CHECKSTYLE:ON LineLength + } + + private String generateAcceptMethod(String symbolClass) { + // CHECKSTYLE:OFF LineLength + String visitorName = toFirstUpperCase(grammarName) + "ParseTreeVisitor"; + return + " @Override\n" + + " public T accept(ParseTreeVisitor visitor) {\n" + + " if (visitor instanceof " + visitorName + ") {\n" + + " return ((" + visitorName + ") visitor).visit" + toFirstUpperCase(symbolClass) + "(this);\n" + + " }\n" + + " return visitor.visitNonterminalNode(this);\n" + + " }\n"; + // CHECKSTYLE:ON LineLength + } + + private String generateSymbols(List symbols) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < symbols.size(); i++) { + Symbol symbol = symbols.get(i); + String type = getSymbolType(symbol); + // When type == null, it's a data-dependent construct that does not contribute to parse trees + if (type != null) { + String label = getLabel(symbol); + if (label != null) { + sb.append(" public " + toFirstUpperCase(type) + " " + label + "() {\n"); + sb.append(" return (" + toFirstUpperCase(type) + ") childAt(" + i + ");\n"); + sb.append(" }\n\n"); + } + } + } + return sb.toString(); + } + + private String getSymbolType(Symbol symbol) { + if (symbol instanceof Nonterminal) { + return symbol.getName(); + } else if (symbol instanceof Terminal) { + return TerminalNode.class.getSimpleName(); + } else if (symbol instanceof Star) { + return StarNode.class.getSimpleName(); + } else if (symbol instanceof Plus) { + return PlusNode.class.getSimpleName(); + } else if (symbol instanceof Group) { + return GroupNode.class.getSimpleName(); + } else if (symbol instanceof Alt) { + return AltNode.class.getSimpleName(); + } else if (symbol instanceof Opt) { + return OptionNode.class.getSimpleName(); + } else if (symbol instanceof Start) { + return StartNode.class.getSimpleName(); + } else if (symbol instanceof Code) { + return getSymbolType(((Code) symbol).getSymbol()); + } else { + // Data dependent symbols do not have a type + return null; + } + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/generator/parser/ParserGenerator.java b/benchmarks/src/main/kotlin/org/iguana/generator/parser/ParserGenerator.java new file mode 100644 index 000000000..9b7289115 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/generator/parser/ParserGenerator.java @@ -0,0 +1,93 @@ +package org.iguana.generator.parser; + +import org.iguana.generator.Generator; + +import static org.iguana.generator.GeneratorUtils.writeToJavaFile; +import static org.iguana.utils.string.StringUtil.toFirstUpperCase; + +public class ParserGenerator extends Generator { + + public ParserGenerator(String grammarName, String packageName, String genDirectory) { + super(null, grammarName, packageName, genDirectory); + } + + // CHECKSTYLE:OFF: LineLength + public void generateGrammar() { + String className = toFirstUpperCase(grammarName); + String content = + "// This file has been generated, do not directly edit this file!\n" + + "\n" + + "package " + packageName + ";\n" + + "\n" + + "import org.iguana.grammar.Grammar;\n" + + "import org.iguana.util.serialization.JsonSerializer;\n" + + "\n" + + "import java.io.IOException;\n" + + "import java.io.InputStream;\n" + + "\n" + + "public class " + className + "Grammar {\n" + + "\n" + + " private static final String grammarName = \"" + grammarName + "\";\n" + + "\n" + + " private static Grammar grammar;\n" + + "\n" + + " public static Grammar getGrammar() {\n" + + " if (grammar == null) {\n" + + " grammar = loadGrammar();\n" + + " }\n" + + " return grammar;\n" + + " }\n" + + "\n" + + " private static Grammar loadGrammar() {\n" + + " String grammarJsonFile = grammarName + \".json\";\n" + + " try (InputStream in = " + className + "Parser.class.getResourceAsStream(\"/\" + " + "grammarJsonFile)) {\n" + + " if (in == null) throw new RuntimeException(\"Grammar json file \" + grammarJsonFile + \" is not found.\");\n" + + " return JsonSerializer.deserialize(in, Grammar.class);\n" + + " } catch (IOException e) {\n" + + " throw new RuntimeException(e);\n" + + " }\n" + + " }\n" + + "}\n"; + writeToJavaFile(content, genDirectory, className + "Grammar"); + System.out.println(className + "Grammar" + " has been generated in " + genDirectory + "/" + className + "Grammar.java"); + } + // CHECKSTYLE:ON: LineLength + + public void generateParser() { + String className = toFirstUpperCase(grammarName); + String content = + "// This file has been generated, do not directly edit this file!\n" + + "\n" + + "package " + packageName + ";\n" + + "\n" + + "import org.iguana.grammar.Grammar;\n" + + "import org.iguana.parser.IguanaParser;\n" + + "import org.iguana.parsetree.ParseTreeBuilder;\n" + + "import org.iguana.parsetree.ParseTreeNode;\n" + + "import org.iguana.utils.input.Input;\n" + + "\n" + + "public class " + className + "Parser extends IguanaParser {\n" + + "\n" + + " private static " + className + "Parser parser;\n" + + "\n" + + " private " + className + "Parser(Grammar grammar) {\n" + + " super(grammar);\n" + + " }\n" + + "\n" + + " public static " + className + "Parser getInstance() {\n" + + " if (parser == null) {\n" + + " parser = new " + className + "Parser(" + className + "Grammar.getGrammar());\n" + + " }\n" + + " return parser;\n" + + " }\n" + + "\n" + + " @Override\n" + + " protected ParseTreeBuilder getParseTreeBuilder(Input input) {\n" + + " return new " + className + "ParseTreeBuilder(input);\n" + + " }\n" + + "}\n"; + writeToJavaFile(content, genDirectory, className + "Parser"); + System.out.println( + className + "Parser" + " has been generated in " + genDirectory + "/" + className + "Parser.java"); + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/grammar/AbstractGrammarGraphSymbolVisitor.java b/benchmarks/src/main/kotlin/org/iguana/grammar/AbstractGrammarGraphSymbolVisitor.java new file mode 100644 index 000000000..5d9e61b3e --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/grammar/AbstractGrammarGraphSymbolVisitor.java @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.grammar; + +import org.iguana.grammar.exception.UnexpectedSymbolException; +import org.iguana.grammar.symbol.Align; +import org.iguana.grammar.symbol.Alt; +import org.iguana.grammar.symbol.Block; +import org.iguana.grammar.symbol.Group; +import org.iguana.grammar.symbol.IfThen; +import org.iguana.grammar.symbol.IfThenElse; +import org.iguana.grammar.symbol.Ignore; +import org.iguana.grammar.symbol.Offside; +import org.iguana.grammar.symbol.Opt; +import org.iguana.grammar.symbol.Plus; +import org.iguana.grammar.symbol.Star; +import org.iguana.grammar.symbol.Start; +import org.iguana.grammar.symbol.While; +import org.iguana.traversal.ISymbolVisitor; + +public abstract class AbstractGrammarGraphSymbolVisitor implements ISymbolVisitor { + + @Override + public T visit(Align symbol) { + throw new UnexpectedSymbolException(symbol, "grammar-to-graph transformation"); + } + + @Override + public T visit(Block symbol) { + throw new UnexpectedSymbolException(symbol, "grammar-to-graph transformation"); + } + + @Override + public T visit(IfThen symbol) { + throw new UnexpectedSymbolException(symbol, "grammar-to-graph transformation"); + } + + @Override + public T visit(IfThenElse symbol) { + throw new UnexpectedSymbolException(symbol, "grammar-to-graph transformation"); + } + + @Override + public T visit(Ignore symbol) { + throw new UnexpectedSymbolException(symbol, "grammar-to-graph transformation"); + } + + @Override + public T visit(Offside symbol) { + throw new UnexpectedSymbolException(symbol, "grammar-to-graph transformation"); + } + + @Override + public T visit(While symbol) { + throw new UnexpectedSymbolException(symbol, "grammar-to-graph transformation"); + } + + @Override + public T visit(Alt symbol) { + throw new UnexpectedSymbolException(symbol, "grammar-to-graph transformation"); + } + + @Override + public T visit(Opt symbol) { + throw new UnexpectedSymbolException(symbol, "grammar-to-graph transformation"); + } + + @Override + public T visit(Plus symbol) { + throw new UnexpectedSymbolException(symbol, "grammar-to-graph transformation"); + } + + @Override + public T visit(Group symbol) { + throw new UnexpectedSymbolException(symbol, "grammar-to-graph transformation"); + } + + @Override + public T visit(Star symbol) { + throw new UnexpectedSymbolException(symbol, "grammar-to-graph transformation"); + } + + @Override + public T visit(Start symbol) { + throw new UnexpectedSymbolException(symbol, "grammar-to-graph transformation"); + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/grammar/Grammar.java b/benchmarks/src/main/kotlin/org/iguana/grammar/Grammar.java new file mode 100644 index 000000000..7f29b6c31 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/grammar/Grammar.java @@ -0,0 +1,1088 @@ +package org.iguana.grammar; + +import org.iguana.datadependent.ast.Expression; +import org.iguana.grammar.condition.DataDependentCondition; +import org.iguana.grammar.condition.PositionalCondition; +import org.iguana.grammar.condition.RegularExpressionCondition; +import org.iguana.grammar.runtime.AssociativityGroup; +import org.iguana.grammar.runtime.PrecedenceLevel; +import org.iguana.grammar.runtime.Recursion; +import org.iguana.grammar.runtime.RuntimeGrammar; +import org.iguana.grammar.runtime.RuntimeRule; +import org.iguana.grammar.slot.NonterminalNodeType; +import org.iguana.grammar.slot.TerminalNodeType; +import org.iguana.grammar.symbol.Align; +import org.iguana.grammar.symbol.Alt; +import org.iguana.grammar.symbol.Alternative; +import org.iguana.grammar.symbol.Associativity; +import org.iguana.grammar.symbol.Block; +import org.iguana.grammar.symbol.Code; +import org.iguana.grammar.symbol.CodeHolder; +import org.iguana.grammar.symbol.Conditional; +import org.iguana.grammar.symbol.Error; +import org.iguana.grammar.symbol.Group; +import org.iguana.grammar.symbol.Identifier; +import org.iguana.grammar.symbol.IfThen; +import org.iguana.grammar.symbol.IfThenElse; +import org.iguana.grammar.symbol.Ignore; +import org.iguana.grammar.symbol.LayoutStrategy; +import org.iguana.grammar.symbol.Nonterminal; +import org.iguana.grammar.symbol.Offside; +import org.iguana.grammar.symbol.Opt; +import org.iguana.grammar.symbol.Plus; +import org.iguana.grammar.symbol.PriorityLevel; +import org.iguana.grammar.symbol.Return; +import org.iguana.grammar.symbol.Rule; +import org.iguana.grammar.symbol.Sequence; +import org.iguana.grammar.symbol.Star; +import org.iguana.grammar.symbol.Start; +import org.iguana.grammar.symbol.Symbol; +import org.iguana.grammar.symbol.Terminal; +import org.iguana.grammar.symbol.While; +import org.iguana.grammar.transformation.EBNFToBNF; +import org.iguana.grammar.transformation.GrammarVisitor; +import org.iguana.grammar.transformation.ResolveIdentifiers; +import org.iguana.grammar.transformation.TopLevelPrecedenceNonterminals; +import org.iguana.regex.Char; +import org.iguana.regex.CharRange; +import org.iguana.regex.EOF; +import org.iguana.regex.Epsilon; +import org.iguana.regex.InlineReferences; +import org.iguana.regex.Reference; +import org.iguana.regex.RegularExpression; +import org.iguana.regex.Seq; +import org.iguana.regex.visitor.RegularExpressionVisitor; +import org.iguana.traversal.IConditionVisitor; +import org.iguana.traversal.ISymbolVisitor; +import org.iguana.util.serialization.JsonSerializer; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.ListIterator; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; + +import static org.iguana.utils.collections.CollectionsUtil.buildList; +import static org.iguana.utils.collections.CollectionsUtil.buildMap; + +public class Grammar { + + private final List rules; + private final Map regularExpressionDefinitions; + private final Map literals; + private final List startSymbols; + private final Symbol layout; + private final Map globals; + private final String name; + + private RuntimeGrammar runtimeGrammar; + + Grammar(Builder builder) { + this.rules = builder.rules; + this.regularExpressionDefinitions = builder.regularExpressionDefinitions; + this.literals = builder.literals; + this.startSymbols = builder.startSymbols; + this.layout = builder.layout; + this.globals = builder.globals; + this.name = builder.name; + } + + public List getRules() { + return rules; + } + + public Map getRegularExpressionDefinitions() { + return regularExpressionDefinitions; + } + + public Map getLiterals() { + return literals; + } + + public List getStartSymbols() { + return startSymbols; + } + + public Symbol getLayout() { + return layout; + } + + public Map getGlobals() { + return globals; + } + + public String getName() { + return name; + } + + public RuntimeGrammar toRuntimeGrammar() { + if (runtimeGrammar == null) { + Map regularExpressions = InlineReferences.inline(regularExpressionDefinitions); + Set nonterminals = rules.stream().map(r -> r.getHead().getName()).collect(Collectors.toSet()); + ResolveIdentifiers resolveIdentifiers = new ResolveIdentifiers(nonterminals, regularExpressions); + GrammarVisitor grammarVisitor = new GrammarVisitor(resolveIdentifiers); + List newRules = grammarVisitor.transform(rules); + newRules.addAll(TopLevelPrecedenceNonterminals.addTopLevelPrecedenceRules(newRules)); + + Map> leftEnds = new HashMap<>(); + Map> rightEnds = new HashMap<>(); + Set ebnfs = new HashSet<>(); + + computeEnds(leftEnds, rightEnds, ebnfs); + + Set topLevelRegularExpressions = getTopLevelRegularExpressions(this); + + RuntimeGrammar.Builder grammarBuilder = new RuntimeGrammar.Builder(); + for (Rule rule : newRules) { + grammarBuilder.addRules(getRules(rule, leftEnds, rightEnds, ebnfs)); + } + grammarBuilder.setStartSymbols(startSymbols); + + // Resolve the layout symbol + Symbol newLayout = null; + if (layout != null) { + newLayout = layout.accept(resolveIdentifiers); + if (newLayout instanceof Terminal) { + newLayout = ((Terminal) newLayout).copy().setNodeType(TerminalNodeType.Layout).build(); + } else if (newLayout instanceof Nonterminal) { + newLayout = ((Nonterminal) newLayout).copy().setNodeType(NonterminalNodeType.Layout).build(); + } else { + throw new RuntimeException("Layout can only be an instance of a terminal or nonterminal, but was " + + newLayout.getClass().getSimpleName()); + } + } + + grammarBuilder.setLayout(newLayout); + grammarBuilder.setGlobals(globals); + + Map newRegularExpressions = new HashMap<>(); + for (Map.Entry entry : regularExpressions.entrySet()) { + if (topLevelRegularExpressions.contains(entry.getKey())) { + newRegularExpressions.put(entry.getKey(), entry.getValue()); + } + } + + grammarBuilder.setRegularExpressionDefinitions(newRegularExpressions); + grammarBuilder.setLiterals(literals); + + Map> ebnfLefts = new HashMap<>(); + Map> ebnfRights = new HashMap<>(); + + for (String ebnf : ebnfs) { + Set set = leftEnds.get(ebnf); + if (set != null) + ebnfLefts.put(ebnf, set); + + set = rightEnds.get(ebnf); + if (set != null) + ebnfRights.put(ebnf, set); + } + + grammarBuilder.addEBNFl(ebnfLefts); + grammarBuilder.addEBNFr(ebnfRights); + runtimeGrammar = grammarBuilder.build(); + } + + return runtimeGrammar; + } + + private void computeEnds(Map> leftEnds, Map> rightEnds, Set ebnfs) { + for (Rule rule : rules) { + for (PriorityLevel priorityLevel : rule.getPriorityLevels()) { + for (Alternative alternative : priorityLevel.getAlternatives()) { + for (Sequence seq : alternative.seqs()) { + computeEnds(rule.getHead(), seq.getSymbols(), leftEnds, rightEnds, ebnfs); + } + } + } + } + + boolean changed = true; + while (changed) { + changed = false; + for (String head : leftEnds.keySet()) { + Set ends = leftEnds.get(head); + int size = ends.size(); + Set delta = new HashSet<>(); + for (String end : ends) { + Set lefts = leftEnds.get(end); + if (lefts != null) { + for (String left : lefts) { + if (!left.equals(head)) + delta.add(left); + } + } + } + ends.addAll(delta); + if (ends.size() != size) + changed = true; + } + } + + changed = true; + while (changed) { + changed = false; + for (String head : rightEnds.keySet()) { + Set ends = rightEnds.get(head); + int size = ends.size(); + Set delta = new HashSet<>(); + for (String end : ends) { + Set rights = rightEnds.get(end); + if (rights != null) { + for (String right : rights) { + if (!right.equals(head)) + delta.add(right); + } + } + } + ends.addAll(delta); + if (ends.size() != size) + changed = true; + } + } + } + + public static Grammar fromJsonFile(String path) { + try { + return JsonSerializer.deserialize(Files.newInputStream(new File(path).toPath()), Grammar.class); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (!(obj instanceof Grammar)) return false; + Grammar other = (Grammar) obj; + return this.rules.equals(other.rules) && Objects.equals(this.layout, other.layout) + && Objects.equals(this.startSymbols, other.startSymbols); + } + + @Override + public int hashCode() { + return Objects.hash(rules, layout, startSymbols); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + for (Rule rule : rules) { + sb.append(rule).append("\n"); + } + sb.delete(sb.length() - 1, sb.length()); + return sb.toString(); + } + + public static class Builder { + private List rules = new ArrayList<>(); + private Map regularExpressionDefinitions = new LinkedHashMap<>(); + private Map literals = new LinkedHashMap<>(); + private String name; + private List startSymbols = new ArrayList<>(); + private Symbol layout; + private Map globals = new HashMap<>(); + + public Builder addRule(Rule rule) { + this.rules.add(rule); + return this; + } + + public Builder setStartSymbols(List startSymbols) { + this.startSymbols = startSymbols; + return this; + } + + public Builder addStartSymbol(Start startSymbol) { + this.startSymbols.add(startSymbol); + return this; + } + + public Builder setLayout(Symbol layout) { + this.layout = layout; + return this; + } + + public Builder setName(String name) { + this.name = name; + return this; + } + + public Builder addRegularExpression(String name, RegularExpression regularExpression) { + regularExpressionDefinitions.put(name, regularExpression); + return this; + } + + public Builder setLiterals(Map literals) { + this.literals = literals; + return this; + } + + public Builder addLiteral(String name, RegularExpression regularExpression) { + literals.put(name, regularExpression); + return this; + } + + public Builder setGlobals(Map globals) { + this.globals = globals; + return this; + } + + // TODO: differentiate between global variables and top-level variables + public Builder addGlobal(String key, Expression value) { + this.globals.put(key, value); + return this; + } + + public Builder setRegularExpressionDefinitions(Map regularExpressionDefinitions) { + this.regularExpressionDefinitions = regularExpressionDefinitions; + return this; + } + + public Grammar build() { + rules = buildList(rules); + regularExpressionDefinitions = buildMap(regularExpressionDefinitions); + literals = buildMap(literals); + globals = buildMap(globals); + startSymbols = buildList(startSymbols); + return new Grammar(this); + } + } + + private List getRules( + Rule highLevelRule, + Map> leftEnds, + Map> rightEnds, + Set ebnfs) { + List priorityLevels = highLevelRule.getPriorityLevels(); + + List rules = new ArrayList<>(); + PrecedenceLevel level = PrecedenceLevel.getFirst(); + Nonterminal head = highLevelRule.getHead(); + + ListIterator altsIt = priorityLevels.listIterator(priorityLevels.size()); + while (altsIt.hasPrevious()) { + PriorityLevel group = altsIt.previous(); + ListIterator altIt = group.getAlternatives().listIterator(group.getAlternatives().size()); + while (altIt.hasPrevious()) { + Alternative alternative = altIt.previous(); + if (alternative.rest() != null) { // Associativity group + AssociativityGroup assocGroup = new AssociativityGroup(alternative.getAssociativity(), level); + List sequences = new ArrayList<>(Arrays.asList(alternative.first())); + sequences.addAll(alternative.rest()); + ListIterator seqIt = sequences.listIterator(sequences.size()); + while (seqIt.hasPrevious()) { + Sequence sequence = seqIt.previous(); + RuntimeRule rule = getRule(head, sequence.getSymbols(), sequence.getAssociativity(), + sequence.getLabel(), highLevelRule.getLayoutStrategy(), leftEnds, + rightEnds, ebnfs); + int precedence = assocGroup.getPrecedence(rule); + rule = rule.copy() + .setPrecedence(precedence) + .setPrecedenceLevel(level) + .setAssociativityGroup(assocGroup) + .setAttributes(sequence.getAttributes()) + .build(); + rules.add(rule); + } + assocGroup.done(); + level.containsAssociativityGroup(assocGroup.getLhs(), assocGroup.getRhs()); + } else { + List symbols = new ArrayList<>(); + if (alternative.first().isEmpty()) { // Empty alternative + Sequence sequence = alternative.first(); + String label = sequence.getLabel(); + RuntimeRule rule = getRule(head, symbols, Associativity.UNDEFINED, label, + highLevelRule.getLayoutStrategy(), leftEnds, rightEnds, ebnfs); + int precedence = level.getPrecedence(rule); + rule = rule.copy() + .setPrecedence(precedence) + .setPrecedenceLevel(level) + .setAttributes(sequence.getAttributes()) + .build(); + rules.add(rule); + } else { + Sequence sequence = alternative.first(); + symbols.add(sequence.first()); + if (sequence.rest() != null) + addAll(symbols, sequence.rest()); + RuntimeRule rule = getRule(head, symbols, sequence.getAssociativity(), sequence.getLabel(), + highLevelRule.getLayoutStrategy(), leftEnds, rightEnds, ebnfs); + int precedence = level.getPrecedence(rule); + rule = rule.copy() + .setPrecedence(precedence) + .setPrecedenceLevel(level) + .setAttributes(sequence.getAttributes()) + .build(); + rules.add(rule); + } + } + } + level.setUndefinedIfNeeded(); + level = level.getNext(); + } + level.done(); + Collections.reverse(rules); + return rules; + } + + private RuntimeRule getRule( + Nonterminal head, + List body, + Associativity associativity, + String label, + LayoutStrategy layoutStrategy, + Map> leftEnds, + Map> rightEnds, + Set ebnfs) { + boolean isLeft = body.size() != 0 && body.get(0).accept( + new IsRecursive(head, Recursion.LEFT_REC, leftEnds, ebnfs)); + boolean isRight = body.size() != 0 && body.get(body.size() - 1).accept( + new IsRecursive(head, Recursion.RIGHT_REC, leftEnds, ebnfs)); + + IsRecursive visitor = new IsRecursive(head, Recursion.iLEFT_REC, leftEnds, ebnfs); + + boolean isiLeft = body.size() != 0 && body.get(0).accept(visitor); + String leftEnd = visitor.getEnd(); + + visitor = new IsRecursive(head, Recursion.iRIGHT_REC, rightEnds, ebnfs); + boolean isiRight = body.size() != 0 && body.get(body.size() - 1).accept(visitor); + String rightEnd = visitor.getEnd(); + + Recursion recursion = Recursion.NON_REC; + Recursion irecursion = Recursion.NON_REC; + int precedence = -1; + + if (isLeft && isRight) + recursion = Recursion.LEFT_RIGHT_REC; + else if (isLeft) + recursion = Recursion.LEFT_REC; + else if (isRight) + recursion = Recursion.RIGHT_REC; + + if (isiLeft && isiRight) + irecursion = Recursion.iLEFT_RIGHT_REC; + else if (isiLeft) + irecursion = Recursion.iLEFT_REC; + else if (isiRight) + irecursion = Recursion.iRIGHT_REC; + + if (recursion == Recursion.NON_REC && irecursion == Recursion.NON_REC) + associativity = Associativity.UNDEFINED; + + // Mixed cases + boolean isPrefixOrCanBePrefix = (irecursion != Recursion.iLEFT_REC && recursion == Recursion.RIGHT_REC) + || (recursion != Recursion.LEFT_REC && irecursion == Recursion.iRIGHT_REC); + boolean isPostfixOrCanBePostfix = (recursion == Recursion.LEFT_REC && irecursion != Recursion.iRIGHT_REC) + || (irecursion == Recursion.iLEFT_REC && recursion != Recursion.RIGHT_REC); + + if ((isPrefixOrCanBePrefix || isPostfixOrCanBePostfix) && associativity != Associativity.NON_ASSOC) + associativity = Associativity.UNDEFINED; + + if (associativity == null) { + associativity = Associativity.UNDEFINED; + } + + return RuntimeRule.withHead(head) + .addSymbols(body) + .setRecursion(recursion) + .setiRecursion(irecursion) + .setLeftEnd(leftEnd) + .setRightEnd(rightEnd) + .setLeftEnds(leftEnds.get(head.getName())) + .setRightEnds(rightEnds.get(head.getName())) + .setAssociativity(associativity) + .setPrecedence(precedence) + .setLayoutStrategy(layoutStrategy) + .setLabel(label) + .build(); + } + + private void computeEnds( + Nonterminal head, + List symbols, + Map> leftEnds, + Map> rightEnds, + Set ebnfs) { + if (symbols.size() >= 1) { + Symbol first = symbols.get(0); + Symbol last = symbols.get(symbols.size() - 1); + + IsRecursive isLeft = new IsRecursive(head, Recursion.LEFT_REC, ebnfs); + + if (!first.accept(isLeft) && !isLeft.getEnd().isEmpty()) { + Set ends = leftEnds.get(head.getName()); + if (ends == null) { + ends = new HashSet<>(); + leftEnds.put(head.getName(), ends); + } + ends.add(isLeft.getEnd()); + if (!isLeft.ends.isEmpty()) // EBNF related + leftEnds.putAll(isLeft.ends); + } + + IsRecursive isRight = new IsRecursive(head, Recursion.RIGHT_REC, ebnfs); + + if (!last.accept(isRight) && !isRight.getEnd().isEmpty()) { + Set ends = rightEnds.get(head.getName()); + if (ends == null) { + ends = new HashSet<>(); + rightEnds.put(head.getName(), ends); + } + ends.add(isRight.getEnd()); + if (!isRight.ends.isEmpty()) // EBNF related + rightEnds.putAll(isRight.ends); + } + } + } + + private static Set getTopLevelRegularExpressions(Grammar grammar) { + Set references = new LinkedHashSet<>(); + for (Rule rule : grammar.getRules()) { + for (PriorityLevel priorityLevel : rule.getPriorityLevels()) { + for (Alternative alternative : priorityLevel.getAlternatives()) { + for (Sequence seq : alternative.seqs()) { + for (Symbol symbol : seq.getSymbols()) { + GatherTopLevelRegularExpressionsVisitor visitor = + new GatherTopLevelRegularExpressionsVisitor(grammar); + symbol.accept(visitor); + references.addAll(visitor.references); + } + } + } + } + } + return references; + } + + // Top-level regular expressions are the ones that are directly reachable from context free rules. + // They define the tokens of the language. + // TODO: unify this with SymbolToSymbolVisitor + private static class GatherTopLevelRegularExpressionsVisitor + implements ISymbolVisitor, RegularExpressionVisitor, IConditionVisitor { + + private final Set references = new LinkedHashSet<>(); + private final Grammar grammar; + + GatherTopLevelRegularExpressionsVisitor(Grammar grammar) { + this.grammar = grammar; + } + + private Void visitChildren(Symbol symbol) { + symbol.getChildren().forEach(child -> child.accept(this)); + visitConditions(symbol); + return null; + } + + private Void visitConditions(Symbol symbol) { + symbol.getPreConditions().forEach(c -> c.accept(this)); + symbol.getPostConditions().forEach(c -> c.accept(this)); + return null; + } + + private Void visitChildren(RegularExpression regex) { + regex.getChildren().forEach(child -> child.accept(this)); + return null; + } + + @Override + public Void visit(Align align) { + return visitChildren(align); + } + + @Override + public Void visit(Block block) { + return visitChildren(block); + } + + @Override + public Void visit(Code code) { + return visitChildren(code); + } + + @Override + public Void visit(Error error) { + return null; + } + + @Override + public Void visit(Conditional conditional) { + return visitChildren(conditional); + } + + @Override + public Void visit(IfThen ifThen) { + return visitChildren(ifThen); + } + + @Override + public Void visit(IfThenElse ifThenElse) { + return visitChildren(ifThenElse); + } + + @Override + public Void visit(Ignore ignore) { + return visitChildren(ignore); + } + + @Override + public Void visit(Nonterminal nonterminal) { + visitConditions(nonterminal); + return null; + } + + @Override + public Void visit(Offside offside) { + return visitChildren(offside); + } + + @Override + public Void visit(Terminal terminal) { + return null; + } + + @Override + public Void visit(While whileSymbol) { + return visitChildren(whileSymbol); + } + + @Override + public Void visit(Return returnSymbol) { + return visitChildren(returnSymbol); + } + + @Override + public Void visit(Alt alt) { + return visitChildren(alt); + } + + @Override + public Void visit(Opt opt) { + return visitChildren(opt); + } + + @Override + public Void visit(Plus plus) { + return visitChildren(plus); + } + + @Override + public Void visit(Group group) { + return visitChildren(group); + } + + @Override + public Void visit(Star star) { + return visitChildren(star); + } + + @Override + public Void visit(Start start) { + return null; + } + + @Override + public Void visit(Identifier identifier) { + visitConditions(identifier); + if (grammar.getRegularExpressionDefinitions().containsKey(identifier.getName())) { + references.add(identifier.getName()); + } + return null; + } + + @Override + public Void visit(Char c) { + return null; + } + + @Override + public Void visit(CharRange r) { + return null; + } + + @Override + public Void visit(EOF eof) { + return null; + } + + @Override + public Void visit(Epsilon e) { + return null; + } + + @Override + public Void visit(org.iguana.regex.Star s) { + return visitChildren(s); + } + + @Override + public Void visit(org.iguana.regex.Plus p) { + return visitChildren(p); + } + + @Override + public Void visit(org.iguana.regex.Opt o) { + return visitChildren(o); + } + + @Override + public Void visit(org.iguana.regex.Alt alt) { + return visitChildren(alt); + } + + @Override + public Void visit(Seq seq) { + return visitChildren(seq); + } + + @Override + public Void visit(Reference ref) { + references.add(ref.getName()); + return null; + } + + @Override + public Void visit(DataDependentCondition condition) { + return null; + } + + @Override + public Void visit(PositionalCondition condition) { + return null; + } + + @Override + public Void visit(RegularExpressionCondition condition) { + condition.getRegularExpression().accept(this); + return null; + } + } + + private static class IsRecursive implements ISymbolVisitor, RegularExpressionVisitor { + + private final Recursion recursion; + private final Nonterminal head; + + private final Map> ends; + private final Set ebnfs; + + private String end = ""; + + IsRecursive(Nonterminal head, Recursion recursion, Set ebnfs) { + this(head, recursion, new HashMap<>(), ebnfs); + } + + IsRecursive(Nonterminal head, Recursion recursion, Map> ends, Set ebnfs) { + this.recursion = recursion; + this.head = head; + this.ends = ends; + this.ebnfs = ebnfs; + } + + private String getEnd() { + return end; + } + + @Override + public Boolean visit(Align symbol) { + return symbol.getSymbol().accept(this); + } + + @Override + public Boolean visit(Block symbol) { + List symbols = symbol.getSymbols(); + if (recursion == Recursion.LEFT_REC || recursion == Recursion.iLEFT_REC) + return symbols.get(0).accept(this); + else + return symbols.get(symbols.size() - 1).accept(this); + } + + @Override + public Boolean visit(Char symbol) { + return false; + } + + @Override + public Boolean visit(CharRange symbol) { + return false; + } + + @Override + public Boolean visit(Code symbol) { + return symbol.getSymbol().accept(this); + } + + @Override + public Boolean visit(Error error) { + return false; + } + + @Override + public Boolean visit(Conditional symbol) { + return symbol.getSymbol().accept(this); + } + + @Override + public Boolean visit(EOF symbol) { + return false; + } + + @Override + public Boolean visit(Epsilon symbol) { + return false; + } + + @Override + public Boolean visit(IfThen symbol) { + return symbol.getThenPart().accept(this); + } + + @Override + public Boolean visit(IfThenElse symbol) { + return symbol.getThenPart().accept(this) + || symbol.getElsePart().accept(this); + } + + @Override + public Boolean visit(Ignore symbol) { + return symbol.getSymbol().accept(this); + } + + @Override + public Boolean visit(Identifier symbol) { + end = symbol.getName(); + + if (recursion == Recursion.LEFT_REC || recursion == Recursion.RIGHT_REC) { + if (symbol.getName().equals(head.getName())) { + return true; + } + } else { + Set set = ends.get(symbol.getName()); + if (set != null && set.contains(head.getName())) + return true; + } + + return false; + } + + @Override + public Boolean visit(Nonterminal symbol) { + + end = symbol.getName(); + + if (recursion == Recursion.LEFT_REC || recursion == Recursion.RIGHT_REC) { + if (symbol.getName().equals(head.getName()) + && ((head.getParameters() == null && symbol.getArguments() == null) + || (head.getParameters().size() == symbol.getArguments().length))) + return true; + + } else { + Set set = ends.get(symbol.getName()); + if (set != null && set.contains(head.getName())) + return true; + } + + return false; + } + + @Override + public Boolean visit(Offside symbol) { + return symbol.getSymbol().accept(this); + } + + @Override + public Boolean visit(Terminal symbol) { + end = "$" + head.getName(); + return false; + } + + @Override + public Boolean visit(While symbol) { + return symbol.getBody().accept(this); + } + + @Override + public Boolean visit(Return symbol) { + return false; + } + + @Override + public Boolean visit(org.iguana.grammar.symbol.Alt symbol) { + System.out.println("Warning: indirect recursion isn't yet supported for (.|.)."); + return false; + } + + @Override + public Boolean visit(org.iguana.grammar.symbol.Opt symbol) { + System.out.println("Warning: indirect recursion isn't yet supported for options."); + return false; + } + + @Override + public Boolean visit(org.iguana.grammar.symbol.Plus symbol) { + + if (recursion == Recursion.LEFT_REC || recursion == Recursion.RIGHT_REC) { + + IsRecursive visitor = new IsRecursive(head, recursion, ebnfs); + symbol.getSymbol().accept(visitor); + + String name = EBNFToBNF.getName(Nonterminal.withName(visitor.end), symbol.getSeparators(), null) + "+"; + end = name; + + ends.putAll(visitor.ends); + ends.put(name, new HashSet(Arrays.asList(visitor.end))); + ebnfs.add(name); + + return false; + } else { + + IsRecursive visitor = new IsRecursive(head, recursion, ends, ebnfs); + symbol.getSymbol().accept(visitor); + + String name = EBNFToBNF.getName(Nonterminal.withName(visitor.end), symbol.getSeparators(), null) + "+"; + end = name; + + Set set = ends.get(name); + if (set != null && set.contains(head.getName())) + return true; + } + return false; + } + + @Override + public Boolean visit(org.iguana.grammar.symbol.Group symbol) { + + if (recursion == Recursion.LEFT_REC || recursion == Recursion.RIGHT_REC) { + + IsRecursive visitor = new IsRecursive(head, recursion, ebnfs); + + if (recursion == Recursion.LEFT_REC) + symbol.get(0).accept(visitor); + else + symbol.getSymbols().get(symbol.getSymbols().size() - 1).accept(visitor); + + String name = symbol.getName(); + end = name; + + ends.putAll(visitor.ends); + ends.put(name, new HashSet(Arrays.asList(visitor.end))); + ebnfs.add(name); + + } else { + IsRecursive visitor = new IsRecursive(head, recursion, ends, ebnfs); + + if (recursion == Recursion.iLEFT_REC) + symbol.getSymbols().get(0).accept(visitor); + else + symbol.getSymbols().get(symbol.getSymbols().size() - 1).accept(visitor); + + String name = symbol.getName(); + end = name; + + Set set = ends.get(name); + if (set != null && set.contains(head.getName())) + return true; + } + + return false; + } + + @Override + public Boolean visit(org.iguana.grammar.symbol.Star symbol) { + // TODO: not good, there should be also left and right ends + if (recursion == Recursion.LEFT_REC || recursion == Recursion.RIGHT_REC) { + + IsRecursive visitor = new IsRecursive(head, recursion, ebnfs); + symbol.getSymbol().accept(visitor); + + String base = EBNFToBNF.getName(Nonterminal.withName(visitor.end), symbol.getSeparators(), null); + String name = base + "*"; + end = name; + + ends.putAll(visitor.ends); + ends.put(name, new HashSet(Arrays.asList(base + "+"))); + ends.put(base + "+", new HashSet(Arrays.asList(visitor.end))); + ebnfs.add(name); + ebnfs.add(base + "+"); + return false; + } else { + + IsRecursive visitor = new IsRecursive(head, recursion, ends, ebnfs); + symbol.getSymbol().accept(visitor); + + String base = EBNFToBNF.getName(Nonterminal.withName(visitor.end), symbol.getSeparators(), null); + String name = base + "*"; + end = name; + + Set set = ends.get(name); + if (set != null && set.contains(head.getName())) + return true; + } + return false; + } + + @Override + public Boolean visit(org.iguana.regex.Star s) { + return false; + } + + @Override + public Boolean visit(org.iguana.regex.Plus p) { + return false; + } + + @Override + public Boolean visit(org.iguana.regex.Opt o) { + return false; + } + + @Override + public Boolean visit(org.iguana.regex.Alt alt) { + return false; + } + + @Override + public Boolean visit(org.iguana.regex.Seq seq) { + return false; + } + + @Override + public Boolean visit(Start start) { + throw new IllegalStateException(); +// return start.getNonterminal().accept(this); + } + + @Override + public Boolean visit(Reference ref) { + // TODO Auto-generated method stub + return null; + } + + } + + private static void addAll(List symbols, List rest) { + int i = 0; + while (i < rest.size()) { + Symbol current = rest.get(i); + if (i < rest.size() - 1 && rest.get(i + 1) instanceof CodeHolder) { + CodeHolder holder = (CodeHolder) rest.get(i + 1); + symbols.add(Code.code(current, holder.statement)); + i += 2; + } else { + symbols.add(current); + i += 1; + } + } + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/grammar/GrammarGraph.java b/benchmarks/src/main/kotlin/org/iguana/grammar/GrammarGraph.java new file mode 100644 index 000000000..ed59a24aa --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/grammar/GrammarGraph.java @@ -0,0 +1,62 @@ +package org.iguana.grammar; + +import org.iguana.datadependent.ast.Expression; +import org.iguana.grammar.slot.BodyGrammarSlot; +import org.iguana.grammar.slot.GrammarSlot; +import org.iguana.grammar.slot.NonterminalGrammarSlot; +import org.iguana.grammar.slot.TerminalGrammarSlot; +import org.iguana.grammar.symbol.Nonterminal; +import org.iguana.grammar.symbol.Terminal; +import org.iguana.regex.matcher.DFAMatcherFactory; + +import java.util.List; +import java.util.Map; + +import static java.util.stream.Collectors.toList; + +public class GrammarGraph { + + public static final TerminalGrammarSlot epsilonSlot = + new TerminalGrammarSlot(Terminal.epsilon(), new DFAMatcherFactory()); + private final List slots; + private final Map globals; + private final Map nonterminalsMap; + + public GrammarGraph( + List slots, + Map nonterminalsMap, + Map globals) { + this.slots = slots; + this.nonterminalsMap = nonterminalsMap; + this.globals = globals; + } + + public List getNonterminalGrammarSlots() { + return slots.stream().filter(slot -> slot instanceof NonterminalGrammarSlot) + .map(slot -> (NonterminalGrammarSlot) slot).collect(toList()); + } + + public List getTerminalGrammarSlots() { + return slots.stream().filter(slot -> slot instanceof TerminalGrammarSlot) + .map(slot -> (TerminalGrammarSlot) slot).collect(toList()); + } + + public List getBodyGrammarSlots() { + return slots.stream().filter(slot -> slot instanceof BodyGrammarSlot) + .map(slot -> (BodyGrammarSlot) slot).collect(toList()); + } + + public NonterminalGrammarSlot getStartSlot(Nonterminal nonterminal) { + return nonterminalsMap.get(nonterminal); + } + + public void clear() { + for (GrammarSlot slot : slots) { + slot.reset(); + } + } + + public Map getGlobals() { + return globals; + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/grammar/GrammarGraphBuilder.java b/benchmarks/src/main/kotlin/org/iguana/grammar/GrammarGraphBuilder.java new file mode 100644 index 000000000..b909a7275 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/grammar/GrammarGraphBuilder.java @@ -0,0 +1,522 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.grammar; + +import org.iguana.datadependent.ast.Expression; +import org.iguana.datadependent.ast.Statement; +import org.iguana.grammar.condition.Condition; +import org.iguana.grammar.condition.Conditions; +import org.iguana.grammar.condition.ConditionsFactory; +import org.iguana.grammar.exception.IncorrectNumberOfArgumentsException; +import org.iguana.grammar.operations.FirstFollowSets; +import org.iguana.grammar.runtime.Position; +import org.iguana.grammar.runtime.RuntimeGrammar; +import org.iguana.grammar.runtime.RuntimeRule; +import org.iguana.grammar.slot.BodyGrammarSlot; +import org.iguana.grammar.slot.CodeTransition; +import org.iguana.grammar.slot.ConditionalTransition; +import org.iguana.grammar.slot.EndGrammarSlot; +import org.iguana.grammar.slot.EpsilonGrammarSlot; +import org.iguana.grammar.slot.EpsilonTransition; +import org.iguana.grammar.slot.EpsilonTransition.Type; +import org.iguana.grammar.slot.ErrorTransition; +import org.iguana.grammar.slot.GrammarSlot; +import org.iguana.grammar.slot.NonterminalGrammarSlot; +import org.iguana.grammar.slot.NonterminalTransition; +import org.iguana.grammar.slot.ReturnTransition; +import org.iguana.grammar.slot.TerminalGrammarSlot; +import org.iguana.grammar.slot.TerminalTransition; +import org.iguana.grammar.slot.Transition; +import org.iguana.grammar.slot.lookahead.FollowTest; +import org.iguana.grammar.slot.lookahead.RangeTreeFollowTest; +import org.iguana.grammar.symbol.Code; +import org.iguana.grammar.symbol.Conditional; +import org.iguana.grammar.symbol.Error; +import org.iguana.grammar.symbol.Nonterminal; +import org.iguana.grammar.symbol.Return; +import org.iguana.grammar.symbol.Symbol; +import org.iguana.grammar.symbol.Terminal; +import org.iguana.grammar.transformation.VarToInt; +import org.iguana.regex.CharRange; +import org.iguana.regex.matcher.DFAMatcherFactory; +import org.iguana.regex.matcher.MatcherFactory; +import org.iguana.util.Configuration; +import org.iguana.util.Configuration.EnvironmentImpl; +import org.iguana.utils.collections.rangemap.RangeMap; +import org.iguana.utils.collections.rangemap.RangeMapBuilder; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +import static org.iguana.grammar.GrammarGraph.epsilonSlot; + +public class GrammarGraphBuilder { + + private static final MatcherFactory matcherFactory = new DFAMatcherFactory(); + + private final Map nonterminalsMap; + + private final Map terminalsMap; + + private final List bodyGrammarSlots; + + private FirstFollowSets firstFollow; + + private final RuntimeGrammar grammar; + + private final Configuration config; + + private final Map> mapping; + + private Map current; + + private GrammarGraphBuilder(RuntimeGrammar grammar, Configuration config) { + if (config.getEnvImpl() == EnvironmentImpl.ARRAY || config.getEnvImpl() == EnvironmentImpl.INT_ARRAY) { + // TODO: move this transformation to IguanaRecognizer + VarToInt transformer = new VarToInt(); + this.grammar = transformer.transform(grammar); + this.mapping = transformer.getMapping(); + } else { + this.grammar = grammar; + this.mapping = new HashMap<>(); + } + + this.config = config; + this.nonterminalsMap = new LinkedHashMap<>(); + this.terminalsMap = new LinkedHashMap<>(); + this.bodyGrammarSlots = new ArrayList<>(); + } + + public static GrammarGraph from(RuntimeGrammar grammar, Configuration config) { + GrammarGraphBuilder builder = new GrammarGraphBuilder(grammar, config); + builder.convert(); + List grammarSlots = new ArrayList<>(); + grammarSlots.addAll(builder.nonterminalsMap.values()); + grammarSlots.addAll(builder.terminalsMap.values()); + grammarSlots.addAll(builder.bodyGrammarSlots); + return new GrammarGraph(grammarSlots, builder.nonterminalsMap, grammar.getGlobals()); + } + + public static GrammarGraph from(RuntimeGrammar grammar) { + return from(grammar, Configuration.load()); + } + + private void convert() { + this.firstFollow = new FirstFollowSets(this.grammar); + + terminalsMap.put(Terminal.epsilon(), epsilonSlot); + + Set nonterminals = this.grammar.getNonterminals(); + nonterminals.forEach(this::getNonterminalSlot); + + int i = 0; + for (RuntimeRule r : this.grammar.getRules()) { + current = mapping.get(i); + convert(r); + i++; + } + + nonterminals.forEach(this::setFirstFollowTests); + } + + public NonterminalGrammarSlot getHead(Nonterminal start) { + return nonterminalsMap.get(start); + } + + public Collection getNonterminalGrammarSlots() { + return nonterminalsMap.values(); + } + + public Collection getTerminalGrammarSlots() { + return terminalsMap.values(); + } + + private void convert(RuntimeRule rule) { + Nonterminal nonterminal = rule.getHead(); + NonterminalGrammarSlot nonterminalSlot = getNonterminalSlot(nonterminal); + addRule(nonterminalSlot, rule); + } + + private void setFirstFollowTests(Nonterminal nonterminal) { + NonterminalGrammarSlot nonterminalSlot = getNonterminalSlot(nonterminal); + nonterminalSlot.setLookAheadTest(getLookAheadTest(nonterminal, nonterminalSlot)); + nonterminalSlot.setFollowTest(getFollowTest(nonterminal)); + } + + private RangeMap getLookAheadTest( + Nonterminal nonterminal, + NonterminalGrammarSlot nonterminalSlot) { + if (config.getLookAheadCount() == 0) + return i -> nonterminalSlot.getFirstSlots(); + + List alternatives = grammar.getAlternatives(nonterminal); + + RangeMapBuilder builder = new RangeMapBuilder<>(); + + for (int i = 0; i < alternatives.size(); i++) { + RuntimeRule rule = alternatives.get(i); + BodyGrammarSlot firstSlot = nonterminalSlot.getFirstSlots().get(i); + Set set = firstFollow.getPredictionSet(rule, 0); + set.forEach(cr -> builder.put(cr, firstSlot)); + } + + return builder.buildRangeMap(); + } + + private FollowTest getFollowTest(Nonterminal nonterminal) { + if (config.getLookAheadCount() == 0) + return FollowTest.DEFAULT; + + return new RangeTreeFollowTest(firstFollow.getFollowSet(nonterminal)); + } + + private FollowTest getFollowTest(RuntimeRule rule, int i) { + if (config.getLookAheadCount() == 0) + return FollowTest.DEFAULT; + + return new RangeTreeFollowTest(firstFollow.getPredictionSet(rule, i)); + } + + private void addRule(NonterminalGrammarSlot head, RuntimeRule rule) { + BodyGrammarSlot firstSlot = getFirstGrammarSlot(rule, head); + head.addFirstSlot(firstSlot); + + GrammarGraphSymbolVisitor rule2graph = new GrammarGraphSymbolVisitor(head, rule, firstSlot); + + while (rule2graph.hasNext()) + rule2graph.nextSymbol(); + } + + private class GrammarGraphSymbolVisitor extends AbstractGrammarGraphSymbolVisitor { + + private final NonterminalGrammarSlot head; + private final RuntimeRule rule; + + private BodyGrammarSlot currentSlot; + private int i = 0; + + private int j = -1; + + GrammarGraphSymbolVisitor(NonterminalGrammarSlot head, RuntimeRule rule, BodyGrammarSlot currentSlot) { + this.head = head; + this.rule = rule; + this.currentSlot = currentSlot; + } + + boolean hasNext() { + return i < rule.size(); + } + + void nextSymbol() { + j = -1; + visitSymbol(rule.symbolAt(i)); + i++; + } + + public Void visit(Nonterminal symbol) { + NonterminalGrammarSlot nonterminalSlot = getNonterminalSlot(symbol); + + BodyGrammarSlot slot; + if (i == rule.size() - 1 && j == -1) + slot = getEndSlot(rule, i + 1, rule.getPosition(i + 1), head, symbol.getLabel(), symbol.getVariable(), + symbol.getState()); + else + slot = getBodyGrammarSlot(rule, i + 1, rule.getPosition(i + 1), symbol.getLabel(), symbol.getVariable(), + symbol.getState()); + + Expression[] arguments = symbol.getArguments(); + + validateNumberOfArguments(nonterminalSlot.getNonterminal(), arguments); + + List preConditions = (i == 0 && j == -1) ? new ArrayList<>() : symbol.getPreConditions(); + setTransition( + new NonterminalTransition(nonterminalSlot, currentSlot, slot, arguments, getConditions(preConditions))); + + currentSlot = slot; + + return null; + } + + @Override + public Void visit(Conditional symbol) { + + Symbol sym = symbol.getSymbol(); + Expression expression = symbol.getExpression(); + + visitSymbol(sym); + + BodyGrammarSlot thenSlot = getBodyGrammarSlot(rule, i + 1, rule.getPosition(i + 1), null, null, null); + Transition transition = new ConditionalTransition(expression, currentSlot, thenSlot); + setTransition(transition); + currentSlot = thenSlot; + + return null; + } + + @Override + public Void visit(Code symbol) { + + Symbol sym = symbol.getSymbol(); + Statement[] statements = symbol.getStatements(); + + visitSymbol(sym); + + BodyGrammarSlot done = getBodyGrammarSlot(rule, i + 1, rule.getPosition(i + 1), null, null, null); + Transition transition = new CodeTransition(statements, currentSlot, done); + setTransition(transition); + currentSlot = done; + + return null; + } + + @Override + public Void visit(Error error) { + BodyGrammarSlot slot; + + if (i == rule.size() - 1 && j == -1) + slot = getEndSlot(rule, i + 1, rule.getPosition(i + 1), head, null, null, null); + else + slot = getBodyGrammarSlot(rule, i + 1, rule.getPosition(i + 1), null, null, null); + + ErrorTransition transition = new ErrorTransition(currentSlot, slot); + setTransition(transition); + currentSlot = slot; + return null; + } + + public Void visit(Return symbol) { + BodyGrammarSlot done; + if (i != rule.size() - 1) + throw new RuntimeException("Return symbol can only be used at the end of a grammar rule!"); + else { + if (rule.size() == 1) + done = new EpsilonGrammarSlot(rule.getPosition(i + 1), head, epsilonSlot, + ConditionsFactory.DEFAULT); + else + done = getEndSlot(rule, i + 1, rule.getPosition(i + 1), head, null, null, null); + } + + ReturnTransition transition = new ReturnTransition(symbol.getExpression(), currentSlot, done); + setTransition(transition); + currentSlot = done; + + return null; + } + + @Override + public Void visit(Terminal symbol) { + TerminalGrammarSlot terminalSlot = getTerminalGrammarSlot(symbol); + + BodyGrammarSlot slot; + + if (i == rule.size() - 1 && j == -1) + slot = getEndSlot(rule, i + 1, rule.getPosition(i + 1), head, symbol.getLabel(), null, null); + else + slot = getBodyGrammarSlot(rule, i + 1, rule.getPosition(i + 1), symbol.getLabel(), null, null); + + List preConditions = (i == 0 && j == -1) ? Collections.emptyList() : symbol.getPreConditions(); + TerminalTransition transition = getTerminalTransition(terminalSlot, currentSlot, slot, preConditions, + symbol.getPostConditions()); + setTransition(transition); + currentSlot = slot; + + return null; + } + + /** + * Introduces epsilon transitions to handle labels and preconditions/postconditions + */ + private void visitSymbol(Symbol symbol) { + + if (symbol instanceof Nonterminal + || symbol instanceof Terminal + || symbol instanceof Error + || symbol instanceof Return) { // TODO: I think this can be unified + symbol.accept(this); + return; + } + + Conditions preconditions = i == 0 ? ConditionsFactory.DEFAULT : getConditions(symbol.getPreConditions()); + + if (symbol.getLabel() != null) { + BodyGrammarSlot declared = getBodyGrammarSlot(rule, i + 1, rule.getPosition(i), null, null, null); + EpsilonTransition transition = new EpsilonTransition(Type.DECLARE_LABEL, symbol.getLabel(), + preconditions, currentSlot, declared); + setTransition(transition); + currentSlot = declared; + } else { + BodyGrammarSlot checked = getBodyGrammarSlot(rule, i + 1, rule.getPosition(i), null, null, null); + EpsilonTransition transition = new EpsilonTransition(preconditions, currentSlot, checked); + setTransition(transition); + currentSlot = checked; + } + + j += 1; + + symbol.accept(this); + + j -= 1; + + if (symbol.getLabel() != null) { + + BodyGrammarSlot stored; + if (i == rule.size() - 1 && j == -1) + stored = getEndSlot(rule, i + 1, rule.getPosition(i + 1), head, null, null, null); + else + stored = getBodyGrammarSlot(rule, i + 1, rule.getPosition(i + 1), null, null, null); + + EpsilonTransition transition = new EpsilonTransition(Type.STORE_LABEL, symbol.getLabel(), + getConditions(symbol.getPostConditions()), currentSlot, stored); + setTransition(transition); + currentSlot = stored; + } else { + + BodyGrammarSlot checked; + if (i == rule.size() - 1 && j == -1) + checked = getEndSlot(rule, i + 1, rule.getPosition(i + 1), head, null, null, null); + else + checked = getBodyGrammarSlot(rule, i + 1, rule.getPosition(i + 1), null, null, null); + + EpsilonTransition transition = new EpsilonTransition(getConditions(symbol.getPostConditions()), + currentSlot, checked); + setTransition(transition); + currentSlot = checked; + } + } + + private void setTransition(Transition transition) { + transition.origin().setOutTransition(transition); + transition.destination().setInTransition(transition); + } + + } + + private TerminalTransition getTerminalTransition(TerminalGrammarSlot slot, + BodyGrammarSlot origin, + BodyGrammarSlot dest, + List preConditions, + List postConditions) { + + return new TerminalTransition(slot, origin, dest, getConditions(preConditions), getConditions(postConditions)); + } + + private TerminalGrammarSlot getTerminalGrammarSlot(Terminal t) { + return terminalsMap.computeIfAbsent(t, k -> new TerminalGrammarSlot(t, matcherFactory)); + } + + private NonterminalGrammarSlot getNonterminalSlot(Nonterminal nonterminal) { + NonterminalGrammarSlot ntSlot = nonterminalsMap.computeIfAbsent(nonterminal, + k -> new NonterminalGrammarSlot(nonterminal)); + return ntSlot; + } + + private BodyGrammarSlot getFirstGrammarSlot(RuntimeRule rule, NonterminalGrammarSlot nonterminal) { + BodyGrammarSlot slot; + + if (rule.size() == 0) { + slot = new EpsilonGrammarSlot(rule.getPosition(0, 0), nonterminal, epsilonSlot, ConditionsFactory.DEFAULT); + } else { + // TODO: This is not a final solution; in particular, + // not any precondition of the first symbol (due to labels) can currently be moved to the first slot. + List preConditions = new ArrayList<>(rule.symbolAt(0).getPreConditions()); + + slot = new BodyGrammarSlot(rule.getPosition(0, 0), rule.symbolAt(0).getLabel(), null, null, + getConditions(preConditions), FollowTest.DEFAULT); + } + bodyGrammarSlots.add(slot); + return slot; + } + + private BodyGrammarSlot getBodyGrammarSlot( + RuntimeRule rule, + int i, + Position position, + String label, + String variable, + Set state) { + assert (i - 1) < rule.size(); + + BodyGrammarSlot slot; + if (current != null) + slot = new BodyGrammarSlot(position, label, (label != null && !label.isEmpty()) ? current.get(label) : -1, + variable, (variable != null && !variable.isEmpty()) ? current.get(variable) : -1, state, + getConditions(rule.symbolAt(i - 1).getPostConditions()), getFollowTest(rule, i)); + else + slot = new BodyGrammarSlot(position, label, variable, state, + getConditions(rule.symbolAt(i - 1).getPostConditions()), getFollowTest(rule, i)); + + bodyGrammarSlots.add(slot); + return slot; + } + + private BodyGrammarSlot getEndSlot( + RuntimeRule rule, + int i, + Position position, + NonterminalGrammarSlot nonterminal, + String label, + String variable, + Set state) { + assert i == rule.size(); + + BodyGrammarSlot slot; + if (current != null) + slot = new EndGrammarSlot(position, nonterminal, label, + (label != null && !label.isEmpty()) ? current.get(label) : -1, + variable, (variable != null && !variable.isEmpty()) ? current.get(variable) : -1, state, + getConditions(rule.symbolAt(i - 1).getPostConditions()), getFollowTest(rule, i)); + else + slot = new EndGrammarSlot(position, nonterminal, label, variable, state, + getConditions(rule.symbolAt(i - 1).getPostConditions()), getFollowTest(rule, i)); + + bodyGrammarSlots.add(slot); + return slot; + } + + private static void validateNumberOfArguments(Nonterminal nonterminal, Expression[] arguments) { + List parameters = nonterminal.getParameters(); + if ((parameters == null && arguments == null) + || (Objects.requireNonNull(parameters).size() == Objects.requireNonNull(arguments).length)) { + return; + } + + throw new IncorrectNumberOfArgumentsException(nonterminal, arguments); + } + + private Conditions getConditions(List conditions) { + if (conditions.isEmpty()) return ConditionsFactory.DEFAULT; + return ConditionsFactory.getConditions(conditions, matcherFactory); + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/grammar/condition/Condition.java b/benchmarks/src/main/kotlin/org/iguana/grammar/condition/Condition.java new file mode 100644 index 000000000..3d9a756e7 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/grammar/condition/Condition.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.grammar.condition; + +import org.iguana.datadependent.attrs.AbstractAttrs; +import org.iguana.traversal.IConditionVisitor; + + +public abstract class Condition extends AbstractAttrs { + + protected final ConditionType type; + + public Condition(ConditionType type) { + this.type = type; + } + + public ConditionType getType() { + return type; + } + + public abstract T accept(IConditionVisitor visitor); + + /* + * + * Data-dependent GLL parsing + * + */ + public boolean isDataDependent() { + return false; + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/grammar/condition/ConditionType.java b/benchmarks/src/main/kotlin/org/iguana/grammar/condition/ConditionType.java new file mode 100644 index 000000000..e7b29250f --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/grammar/condition/ConditionType.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.grammar.condition; + +public enum ConditionType { + + FOLLOW(">>", "Follow"), + NOT_FOLLOW("!>>", "Not follow"), + FOLLOW_IGNORE_LAYOUT(">>>", "Follow ignoring the layout"), + NOT_FOLLOW_IGNORE_LAYOUT("!>>>", "Not follow ignoring the layout"), + PRECEDE("<<", "Precede"), + NOT_PRECEDE("!<<", "Not precede"), + MATCH("&", "match"), + NOT_MATCH("\\", "Not match"), + END_OF_LINE("$", "End of line"), + START_OF_LINE("^", "Start of line"), + END_OF_FILE("$$", "End of file"), + DATA_DEPENDENT("?", "Data dependent"); + + private final String symbol; + private final String description; + + ConditionType(String symbol, String description) { + this.symbol = symbol; + this.description = description; + } + + public String getDescription() { + return description; + } + + @Override + public String toString() { + return symbol; + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/grammar/condition/Conditions.java b/benchmarks/src/main/kotlin/org/iguana/grammar/condition/Conditions.java new file mode 100644 index 000000000..b93a03de5 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/grammar/condition/Conditions.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.grammar.condition; + +import org.iguana.datadependent.env.GLLEvaluator; +import org.iguana.datadependent.env.IEvaluatorContext; +import org.iguana.grammar.slot.BodyGrammarSlot; +import org.iguana.gss.GSSNode; +import org.iguana.parser.IguanaRuntime; +import org.iguana.result.Result; +import org.iguana.utils.input.Input; + + +public interface Conditions { + + default boolean execute( + Input input, + BodyGrammarSlot slot, + GSSNode u, + int leftExtent, + int rightExtent, + IguanaRuntime runtime) { + return execute(input, slot, u, leftExtent, rightExtent, GLLEvaluator.getDefaultEvaluatorContext(), runtime); + } + + boolean execute( + Input input, + BodyGrammarSlot slot, + GSSNode u, + int leftExtent, + int rightExtent, + IEvaluatorContext ctx, + IguanaRuntime runtime + ); + + default boolean execute( + Input input, + BodyGrammarSlot slot, + GSSNode u, + int inputIndex, + IguanaRuntime runtime) { + return execute(input, slot, u, inputIndex, GLLEvaluator.getDefaultEvaluatorContext(), runtime); + } + + default boolean execute( + Input input, + BodyGrammarSlot slot, + GSSNode u, + int inputIndex, + IEvaluatorContext ctx, + IguanaRuntime runtime) { + return execute(input, slot, u, inputIndex, inputIndex, ctx, runtime); + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/grammar/condition/ConditionsFactory.java b/benchmarks/src/main/kotlin/org/iguana/grammar/condition/ConditionsFactory.java new file mode 100644 index 000000000..52ab9ca0c --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/grammar/condition/ConditionsFactory.java @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.grammar.condition; + +import org.iguana.datadependent.env.IEvaluatorContext; +import org.iguana.grammar.slot.BodyGrammarSlot; +import org.iguana.gss.GSSNode; +import org.iguana.parser.IguanaRuntime; +import org.iguana.regex.matcher.MatcherFactory; +import org.iguana.result.Result; +import org.iguana.traversal.ToSlotActionConditionVisitor; +import org.iguana.utils.input.Input; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +import static org.iguana.utils.string.StringUtil.listToString; + + +public class ConditionsFactory { + + public static Conditions DEFAULT = new Conditions() { + + @Override + public boolean execute( + Input input, + BodyGrammarSlot slot, + GSSNode u, + int leftExtent, + int rightExtent, + IEvaluatorContext ctx, + IguanaRuntime runtime) { + return false; + } + + @Override + public String toString() { + return ""; + } + }; + + public static Conditions getConditions(List conditions, MatcherFactory factory) { + + List list = new ArrayList<>(conditions); + + boolean requiresEnvironment = false; + for (Condition c : list) { + if (c.isDataDependent()) { + requiresEnvironment = true; + break; + } + } + + ToSlotActionConditionVisitor visitor = new ToSlotActionConditionVisitor(factory); + List actions = list.stream().map(c -> c.accept(visitor)).collect(Collectors.toList()); + + if (requiresEnvironment) { + return new Conditions() { + + @Override + public boolean execute( + Input input, + BodyGrammarSlot slot, + GSSNode gssNode, + int lefExtent, + int rightExtent, + IEvaluatorContext ctx, + IguanaRuntime runtime) { + for (int j = 0; j < actions.size(); j++) { + SlotAction slotAction = actions.get(j); + if (slotAction.execute(input, slot, gssNode, lefExtent, rightExtent, ctx)) { + runtime.recordParseError(rightExtent, input, slot, gssNode, slotAction.toString()); + return true; + } + } + return false; + } + + @Override + public String toString() { + return conditions.isEmpty() ? "" : "[" + listToString(conditions, ";") + "]"; + } + + }; + } + + return new Conditions() { + + @Override + public boolean execute( + Input input, + BodyGrammarSlot slot, + GSSNode gssNode, + int leftExtent, + int rightExtent, + IEvaluatorContext ctx, + IguanaRuntime runtime) { + for (int j = 0; j < actions.size(); j++) { + SlotAction slotAction = actions.get(j); + if (slotAction.execute(input, slot, gssNode, leftExtent, rightExtent, ctx)) { + runtime.recordParseError(rightExtent, input, slot, gssNode, slotAction.toString()); + return true; + } + } + return false; + } + + @Override + public String toString() { + return conditions.isEmpty() ? "" : "[" + listToString(conditions, ";") + "]"; + } + }; + + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/grammar/condition/DataDependentCondition.java b/benchmarks/src/main/kotlin/org/iguana/grammar/condition/DataDependentCondition.java new file mode 100644 index 000000000..1e98f25ba --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/grammar/condition/DataDependentCondition.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.grammar.condition; + +import org.iguana.traversal.IConditionVisitor; + +public class DataDependentCondition extends Condition { + + private final org.iguana.datadependent.ast.Expression expression; + + private DataDependentCondition(ConditionType type, org.iguana.datadependent.ast.Expression expression) { + super(type); + this.expression = expression; + } + + public org.iguana.datadependent.ast.Expression getExpression() { + return expression; + } + + @Override + public boolean isDataDependent() { + return true; + } + + public static DataDependentCondition predicate(org.iguana.datadependent.ast.Expression expression) { + return new DataDependentCondition(ConditionType.DATA_DEPENDENT, expression); + } + + @Override + public String toString() { + return String.format("[%s]", expression); + } + + @Override + public T accept(IConditionVisitor visitor) { + return visitor.visit(this); + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/grammar/condition/PositionalCondition.java b/benchmarks/src/main/kotlin/org/iguana/grammar/condition/PositionalCondition.java new file mode 100644 index 000000000..6fe8e77e4 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/grammar/condition/PositionalCondition.java @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.grammar.condition; + +import org.iguana.traversal.IConditionVisitor; + +/** + * + * @author Ali Afroozeh + * + */ +public class PositionalCondition extends Condition { + + public PositionalCondition(ConditionType type) { + super(type); + } + + @Override + public String toString() { + return type.toString(); + } + + @Override + public boolean equals(Object obj) { + + if (this == obj) return true; + + if (!(obj instanceof PositionalCondition)) return false; + + PositionalCondition other = (PositionalCondition) obj; + + return type == other.type; + } + + @Override + public int hashCode() { + return type.hashCode(); + } + + @Override + public T accept(IConditionVisitor visitor) { + return visitor.visit(this); + } + + public static PositionalCondition startOfLineCondition() { + return new PositionalCondition(ConditionType.START_OF_LINE); + } + + public static PositionalCondition endOfLineCondition() { + return new PositionalCondition(ConditionType.END_OF_LINE); + } + + public static PositionalCondition endOfFileCondition() { + return new PositionalCondition(ConditionType.END_OF_FILE); + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/grammar/condition/RegularExpressionCondition.java b/benchmarks/src/main/kotlin/org/iguana/grammar/condition/RegularExpressionCondition.java new file mode 100644 index 000000000..839bf96e2 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/grammar/condition/RegularExpressionCondition.java @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.grammar.condition; + +import org.iguana.regex.RegularExpression; +import org.iguana.traversal.IConditionVisitor; + +/** + * Conditions relating to the keyword exclusions or follow restrictions. + * For example, Id !>> "[]" or Id \ "if" + * + * @author Ali Afroozeh + * + */ +public class RegularExpressionCondition extends Condition { + + private final RegularExpression regularExpression; + + public RegularExpressionCondition(ConditionType type, RegularExpression regularExpression) { + super(type); + this.regularExpression = regularExpression; + } + + @Override + public String toString() { + return type.toString() + " " + regularExpression; + } + + public RegularExpression getRegularExpression() { + return regularExpression; + } + + @Override + public boolean equals(Object obj) { + + if (this == obj) + return true; + + if (!(obj instanceof RegularExpressionCondition)) + return false; + + RegularExpressionCondition other = (RegularExpressionCondition) obj; + + return type == other.type && regularExpression.equals(other.regularExpression); + } + + @Override + public int hashCode() { + return type.hashCode() * 31 + regularExpression.hashCode(); + } + + public static RegularExpressionCondition notMatch(RegularExpression regularExpression) { + return new RegularExpressionCondition(ConditionType.NOT_MATCH, regularExpression); + } + + public static RegularExpressionCondition match(RegularExpression regularExpression) { + return new RegularExpressionCondition(ConditionType.MATCH, regularExpression); + } + + public static RegularExpressionCondition notFollow(RegularExpression regularExpression) { + return new RegularExpressionCondition(ConditionType.NOT_FOLLOW, regularExpression); + } + + public static RegularExpressionCondition followIgnoreLayout(RegularExpression regularExpression) { + return new RegularExpressionCondition(ConditionType.FOLLOW_IGNORE_LAYOUT, regularExpression); + } + + public static RegularExpressionCondition notFollowIgnoreLayout(RegularExpression regularExpression) { + return new RegularExpressionCondition(ConditionType.NOT_FOLLOW_IGNORE_LAYOUT, regularExpression); + } + + public static RegularExpressionCondition follow(RegularExpression regularExpression) { + return new RegularExpressionCondition(ConditionType.FOLLOW, regularExpression); + } + + public static RegularExpressionCondition precede(RegularExpression regularExpression) { + return new RegularExpressionCondition(ConditionType.PRECEDE, regularExpression); + } + + public static RegularExpressionCondition notPrecede(RegularExpression regularExpression) { + return new RegularExpressionCondition(ConditionType.NOT_PRECEDE, regularExpression); + } + + @Override + public T accept(IConditionVisitor visitor) { + return visitor.visit(this); + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/grammar/condition/SlotAction.java b/benchmarks/src/main/kotlin/org/iguana/grammar/condition/SlotAction.java new file mode 100644 index 000000000..f29c1ac50 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/grammar/condition/SlotAction.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.grammar.condition; + +import org.iguana.datadependent.env.GLLEvaluator; +import org.iguana.datadependent.env.IEvaluatorContext; +import org.iguana.grammar.slot.BodyGrammarSlot; +import org.iguana.gss.GSSNode; +import org.iguana.result.Result; +import org.iguana.utils.input.Input; + +@FunctionalInterface +public interface SlotAction { + + boolean execute( + Input input, + BodyGrammarSlot slot, + GSSNode gssNode, + int leftExtent, + int rightExtent, + IEvaluatorContext ctx + ); + + default boolean execute(Input input, BodyGrammarSlot slot, GSSNode gssNode, int inputIndex) { + return execute(input, slot, gssNode, inputIndex, inputIndex, GLLEvaluator.getDefaultEvaluatorContext()); + } + + default boolean execute( + Input input, + BodyGrammarSlot slot, + GSSNode gssNode, + int inputIndex, + IEvaluatorContext ctx) { + return execute(input, slot, gssNode, inputIndex, inputIndex, ctx); + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/grammar/exception/AssertionFailedException.java b/benchmarks/src/main/kotlin/org/iguana/grammar/exception/AssertionFailedException.java new file mode 100644 index 000000000..e9cec4786 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/grammar/exception/AssertionFailedException.java @@ -0,0 +1,11 @@ +package org.iguana.grammar.exception; + +import org.iguana.datadependent.ast.Expression; + +@SuppressWarnings("serial") +public class AssertionFailedException extends RuntimeException { + + public AssertionFailedException(Expression expression) { + super(String.format("Assertion failed: expression %s evaluated to false.", expression)); + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/grammar/exception/GrammarValidationException.java b/benchmarks/src/main/kotlin/org/iguana/grammar/exception/GrammarValidationException.java new file mode 100644 index 000000000..dc2579f7d --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/grammar/exception/GrammarValidationException.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.grammar.exception; + +import java.util.Set; + +import static org.iguana.utils.string.StringUtil.listToString; + +@SuppressWarnings("serial") +public class GrammarValidationException extends RuntimeException { + + public GrammarValidationException(Set exceptions) { + super(getMessage(exceptions)); + } + + private static String getMessage(Set exceptions) { + return listToString(exceptions, ","); + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/grammar/exception/IncorrectNumberOfArgumentsException.java b/benchmarks/src/main/kotlin/org/iguana/grammar/exception/IncorrectNumberOfArgumentsException.java new file mode 100644 index 000000000..683f5e988 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/grammar/exception/IncorrectNumberOfArgumentsException.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.grammar.exception; + +import org.iguana.datadependent.ast.Expression; +import org.iguana.grammar.symbol.Nonterminal; + +@SuppressWarnings("serial") +public class IncorrectNumberOfArgumentsException extends RuntimeException { + + public IncorrectNumberOfArgumentsException(Nonterminal nonterminal, Expression[] arguments) { + super("Incorrect number of arguments passed to nonterminal " + nonterminal + ": " + arguments.length + + " instead of " + nonterminal.getParameters().size()); + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/grammar/exception/NonterminalNotDefinedException.java b/benchmarks/src/main/kotlin/org/iguana/grammar/exception/NonterminalNotDefinedException.java new file mode 100644 index 000000000..0de9fd322 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/grammar/exception/NonterminalNotDefinedException.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.grammar.exception; + +import org.iguana.grammar.symbol.Nonterminal; + +@SuppressWarnings("serial") +public class NonterminalNotDefinedException extends RuntimeException { + + private final Nonterminal nonterminal; + + public NonterminalNotDefinedException(Nonterminal nonterminal) { + super(nonterminal + " not defined."); + this.nonterminal = nonterminal; + } + + @Override + public int hashCode() { + return nonterminal.hashCode(); + } + + // The equality in this class is important because we put these exceptions in a set and want + // to deduplicate them based on the nonterminals. + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (!(obj instanceof NonterminalNotDefinedException)) return false; + NonterminalNotDefinedException other = (NonterminalNotDefinedException) obj; + return nonterminal.equals(other.nonterminal); + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/grammar/exception/UndeclaredVariableException.java b/benchmarks/src/main/kotlin/org/iguana/grammar/exception/UndeclaredVariableException.java new file mode 100644 index 000000000..b034ac96f --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/grammar/exception/UndeclaredVariableException.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.grammar.exception; + +@SuppressWarnings("serial") +public class UndeclaredVariableException extends RuntimeException { + + public UndeclaredVariableException(String name) { + super("Undeclared variable: " + name); + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/grammar/exception/UndefinedRuntimeValueException.java b/benchmarks/src/main/kotlin/org/iguana/grammar/exception/UndefinedRuntimeValueException.java new file mode 100644 index 000000000..f62dd5ad8 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/grammar/exception/UndefinedRuntimeValueException.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.grammar.exception; + +@SuppressWarnings("serial") +public class UndefinedRuntimeValueException extends RuntimeException { + + public static UndefinedRuntimeValueException instance = new UndefinedRuntimeValueException(); + + private UndefinedRuntimeValueException() { + super("Undefined runtime value."); + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/grammar/exception/UnexpectedRuntimeTypeException.java b/benchmarks/src/main/kotlin/org/iguana/grammar/exception/UnexpectedRuntimeTypeException.java new file mode 100644 index 000000000..1653fbac7 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/grammar/exception/UnexpectedRuntimeTypeException.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.grammar.exception; + +import org.iguana.datadependent.ast.Expression; + +@SuppressWarnings("serial") +public class UnexpectedRuntimeTypeException extends RuntimeException { + + public UnexpectedRuntimeTypeException(Expression expression) { + super("Unexpected runtime type of a value: " + expression); + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/grammar/exception/UnexpectedSymbolException.java b/benchmarks/src/main/kotlin/org/iguana/grammar/exception/UnexpectedSymbolException.java new file mode 100644 index 000000000..8c2e4f658 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/grammar/exception/UnexpectedSymbolException.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.grammar.exception; + +import org.iguana.grammar.symbol.Symbol; + +@SuppressWarnings("serial") +public class UnexpectedSymbolException extends RuntimeException { + + public UnexpectedSymbolException(Symbol symbol) { + super("Unexpected symbol " + symbol); + } + + public UnexpectedSymbolException(Symbol symbol, String phase) { + super(String.format( + "Unexpected symbol %s has been encountered in %s. This symbol must be desugared prior to this phase.", + symbol, phase)); + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/grammar/exception/UnexpectedTypeOfArgumentException.java b/benchmarks/src/main/kotlin/org/iguana/grammar/exception/UnexpectedTypeOfArgumentException.java new file mode 100644 index 000000000..777334457 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/grammar/exception/UnexpectedTypeOfArgumentException.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.grammar.exception; + +import org.iguana.datadependent.ast.Expression; + +@SuppressWarnings("serial") +public class UnexpectedTypeOfArgumentException extends RuntimeException { + + public UnexpectedTypeOfArgumentException(Expression expression) { + super("Unexpected type of an operand: " + expression); + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/grammar/operations/FirstFollowSets.java b/benchmarks/src/main/kotlin/org/iguana/grammar/operations/FirstFollowSets.java new file mode 100644 index 000000000..876df1149 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/grammar/operations/FirstFollowSets.java @@ -0,0 +1,462 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.grammar.operations; + +import org.iguana.grammar.AbstractGrammarGraphSymbolVisitor; +import org.iguana.grammar.runtime.RuntimeGrammar; +import org.iguana.grammar.runtime.RuntimeRule; +import org.iguana.grammar.symbol.Code; +import org.iguana.grammar.symbol.Conditional; +import org.iguana.grammar.symbol.Error; +import org.iguana.grammar.symbol.Nonterminal; +import org.iguana.grammar.symbol.Return; +import org.iguana.grammar.symbol.Symbol; +import org.iguana.grammar.symbol.Terminal; +import org.iguana.regex.CharRange; +import org.iguana.regex.EOF; +import org.iguana.regex.Epsilon; +import org.iguana.traversal.ISymbolVisitor; +import org.iguana.util.Tuple; + +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * + * @author Ali Afroozeh + * + */ +public class FirstFollowSets { + + private final Map> definitions; + + private final Map> firstSets; + + private final Map> followSets; + + private final Map, Set> predictionSets; + + private final Set nullableNonterminals; + + private final ISymbolVisitor> firstSetVisitor; + + private final ISymbolVisitor nullableVisitor; + + private final ISymbolVisitor nonterminalVisitor; + + public FirstFollowSets(RuntimeGrammar grammar) { + this.definitions = grammar.getDefinitions(); + this.firstSets = new HashMap<>(); + this.nullableNonterminals = new HashSet<>(); + this.followSets = new HashMap<>(); + this.predictionSets = new HashMap<>(); + + this.firstSetVisitor = new FirstSymbolVisitor(firstSets); + this.nonterminalVisitor = new NonterminalVisitor(); + this.nullableVisitor = new NullableSymbolVisitor(nullableNonterminals); + + definitions.keySet().forEach(k -> { firstSets.put(k, new HashSet<>()); followSets.put(k, new HashSet<>()); }); + + calculateNullables(); + calculateFirstSets(); + calculateFollowSets(); + calcualtePredictionSets(); + } + + public Map> getFirstSets() { + return firstSets; + } + + public Map> getFollowSets() { + return followSets; + } + + public Map, Set> getPredictionSets() { + return predictionSets; + } + + public Set getNullableNonterminals() { + return nullableNonterminals; + } + + public Set getFirstSet(Nonterminal nonterminal) { + Set firstSet = new HashSet<>(firstSets.get(nonterminal)); + if (isNullable(nonterminal)) + firstSet.addAll(Epsilon.getInstance().getFirstSet()); + return firstSet; + } + + public Set getFollowSet(Nonterminal nonterminal) { + return followSets.get(nonterminal); + } + + public Set getPredictionSet(RuntimeRule rule, int index) { + return predictionSets.get(Tuple.of(rule, index)); + } + + private void calculateFirstSets() { + + Set nonterminals = definitions.keySet(); + + boolean changed = true; + + while (changed) { + + changed = false; + + for (Nonterminal head : nonterminals) { + Set firstSet = firstSets.get(head); + for (RuntimeRule alternate : definitions.get(head)) { + changed |= addFirstSet(firstSet, alternate.getBody(), 0); + } + } + } + } + + private void calculateNullables() { + Set nonterminals = definitions.keySet(); + + boolean changed = true; + + while (changed) { + changed = false; + + for (Nonterminal head : nonterminals) { + for (RuntimeRule rule : definitions.get(head)) { + if (rule.size() == 0 || rule.getBody().stream().allMatch(s -> isNullable(s))) { + changed |= nullableNonterminals.add(head); + break; + } + } + } + } + } + + /** + * Adds the first set of the current slot to the given set. + * + * @return true if adding any new terminals are added to the first set. + */ + private boolean addFirstSet(Set firstSet, List alternative, int index) { + + boolean changed = false; + + if (alternative == null) return false; + + for (int i = index; i < alternative.size(); i++) { + Symbol symbol = alternative.get(i); + changed |= firstSet.addAll(symbol.accept(firstSetVisitor)); + if (!isNullable(symbol)) break; + } + + return changed; + } + + public boolean isNullable(Symbol symbol) { + return symbol.accept(nullableVisitor); + } + + /** + * + * Checks if a grammar slot is nullable. This check is performed until + * the end of the alternate: isChainNullable(X ::= alpha . beta) says if the + * part beta is nullable. + * + */ + private boolean isChainNullable(List alternate, int index) { + if (index >= alternate.size()) return true; + + for (int i = index; i < alternate.size(); i++) { + if (!isNullable(alternate.get(i))) return false; + } + + return true; + } + + private void calculateFollowSets() { + + Set nonterminals = definitions.keySet(); + + boolean changed = true; + + while (changed) { + + changed = false; + + for (Nonterminal head : nonterminals) { + + for (RuntimeRule rule : definitions.get(head)) { + List alternative = rule.getBody(); + + if (alternative == null || alternative.size() == 0) continue; + + for (int i = 0; i < alternative.size(); i++) { + + Symbol symbol = alternative.get(i); + + Nonterminal nonterminal = symbol.accept(nonterminalVisitor); + + if (nonterminal != null) { + // For rules of the form X ::= alpha B beta, add the + // first set of beta to the follow set of B. + Set followSet = followSets.get(nonterminal); + changed |= addFirstSet(followSet, alternative, i + 1); + + // If beta is nullable, then add the follow set of X + // to the follow set of B. + if (isChainNullable(alternative, i + 1)) { + changed |= followSet.addAll(followSets.get(head)); + } + } + } + } + } + } + + for (Nonterminal head : nonterminals) { + // Add the EOF to all nonterminals as each nonterminal can be used + // as the start symbol. + followSets.get(head).addAll(EOF.getInstance().getFirstSet()); + } + } + + private void calcualtePredictionSets() { + + for (Nonterminal nonterminal : definitions.keySet()) { + List rules = definitions.get(nonterminal); + + for (RuntimeRule rule : rules) { + for (int i = 0; i <= rule.size(); i++) { + calculatePredictionSet(rule, i); + } + } + } + } + + private void calculatePredictionSet(RuntimeRule rule, int index) { + + Tuple position = Tuple.of(rule, index); + List alternate = rule.getBody(); + + if (alternate == null) + return; + + int i; + for (i = index; i < alternate.size(); i++) { + + Symbol symbol = alternate.get(i); + + Set firstSet = symbol.accept(firstSetVisitor); + predictionSets.computeIfAbsent(position, k -> new HashSet<>()).addAll(firstSet); + if (!isNullable(symbol)) break; + } + + // if reaching the end of the alternative +// if (isChainNullable(alternate, 0)) { + if (i == alternate.size()) + predictionSets.computeIfAbsent(position, k -> new HashSet<>()).addAll(followSets.get(rule.getHead())); + } + +// public Set calculateLLNonterminals() { +// +// Set nonterminals = definitions.keySet(); +// +// Set ll1Nonterminals = new HashSet<>(); +// +// Set ll1SubGrammarNonterminals = new HashSet<>(); +// +// // Calculating character level predictions +// Map, Set> predictions = new HashMap<>(); +// +// for (Nonterminal head : nonterminals) { +// +// int alternateIndex = 0; +// for(Rule rule : definitions.get(head)) { +// +// List alt = rule.getBody(); +// +// // Calculate the prediction set for the alternate +// Set s = new HashSet<>(); +// addFirstSet(head, s, alt, 0); +// if(s.contains(Epsilon.getInstance())) { +// s.addAll(followSets.get(head)); +// } +// +// // Expand ranges into integers +// Set set = new HashSet<>(); +// for(RegularExpression r : s) { +// set.addAll(convert(r.getFirstSet())); +// } +// +// predictions.put(Tuple.of(head, alternateIndex), set); +// +// alternateIndex++; +// } +// } +// +// for (Nonterminal head : nonterminals) { +// if(isLL1(head, predictions)) { +// ll1Nonterminals.add(head); +// } +// } +// +// for (Nonterminal head : nonterminals) { +// if(ll1Nonterminals.contains(head)) { +// boolean ll1SubGrammar = true; +// for(Nonterminal reachableHead : reachabilityGraph.get(head)) { +// if(!ll1Nonterminals.contains(reachableHead)) { +// ll1SubGrammar = false; +// } +// } +// if(ll1SubGrammar) { +// ll1SubGrammarNonterminals.add(head); +// } +// } +// } +// +// return ll1SubGrammarNonterminals; +// } + + @SuppressWarnings("unused") + private boolean isLL1(Nonterminal nonterminal, Map, Set> predictions) { + + int size = definitions.get(nonterminal).size(); + + // If there is only one alternate + if (size == 1) { + return true; + } + + for (int i = 0; i < size; i++) { + for (int j = 0; j < size; j++) { + if (i != j) { + HashSet intersection = new HashSet<>(predictions.get(Tuple.of(nonterminal, i))); + intersection.retainAll(predictions.get(Tuple.of(nonterminal, j))); + if (!intersection.isEmpty()) { + return false; + } + } + } + } + + return true; + } + + private static class FirstSymbolVisitor extends AbstractGrammarGraphSymbolVisitor> { + + private final Map> firstSets; + + FirstSymbolVisitor(Map> firstSets) { + this.firstSets = firstSets; + } + + @Override + public Set visit(Code symbol) { return symbol.getSymbol().accept(this); } + + @Override + public Set visit(Error error) { + return Collections.emptySet(); + } + + @Override + public Set visit(Conditional symbol) { return symbol.getSymbol().accept(this); } + + @Override + public Set visit(Nonterminal symbol) { return new HashSet<>(firstSets.get(symbol)); } + + @Override + public Set visit(Terminal symbol) { + return symbol.getRegularExpression().getFirstSet(); + } + + @Override + public Set visit(Return symbol) { return Collections.emptySet(); } + + } + + private static class NonterminalVisitor extends AbstractGrammarGraphSymbolVisitor { + @Override + public Nonterminal visit(Code symbol) { return symbol.getSymbol().accept(this); } + + @Override + public Nonterminal visit(Error error) { + return null; + } + + @Override + public Nonterminal visit(Conditional symbol) { return symbol.getSymbol().accept(this); } + + @Override + public Nonterminal visit(Nonterminal symbol) { return symbol; } + + @Override + public Nonterminal visit(Terminal symbol) { + return null; + } + + @Override + public Nonterminal visit(Return symbol) { return null; } + + } + + private static class NullableSymbolVisitor extends AbstractGrammarGraphSymbolVisitor { + + private final Set nullableNonterminals; + + NullableSymbolVisitor(Set nullableNonterminals) { + this.nullableNonterminals = nullableNonterminals; + } + + @Override + public Boolean visit(Code symbol) { return symbol.getSymbol().accept(this); } + + @Override + public Boolean visit(Error error) { + return true; + } + + @Override + public Boolean visit(Conditional symbol) { return symbol.getSymbol().accept(this); } + + @Override + public Boolean visit(Nonterminal symbol) { return nullableNonterminals.contains(symbol); } + + @Override + public Boolean visit(Terminal symbol) { + return symbol.getRegularExpression().isNullable(); + } + + @Override + public Boolean visit(Return symbol) { return true; } + + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/grammar/operations/ReachabilityGraph.java b/benchmarks/src/main/kotlin/org/iguana/grammar/operations/ReachabilityGraph.java new file mode 100644 index 000000000..f99959ec4 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/grammar/operations/ReachabilityGraph.java @@ -0,0 +1,282 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.grammar.operations; + +import org.iguana.grammar.runtime.RuntimeGrammar; +import org.iguana.grammar.runtime.RuntimeRule; +import org.iguana.grammar.symbol.Align; +import org.iguana.grammar.symbol.Alt; +import org.iguana.grammar.symbol.Block; +import org.iguana.grammar.symbol.Code; +import org.iguana.grammar.symbol.Conditional; +import org.iguana.grammar.symbol.Error; +import org.iguana.grammar.symbol.Group; +import org.iguana.grammar.symbol.IfThen; +import org.iguana.grammar.symbol.IfThenElse; +import org.iguana.grammar.symbol.Ignore; +import org.iguana.grammar.symbol.Nonterminal; +import org.iguana.grammar.symbol.Offside; +import org.iguana.grammar.symbol.Opt; +import org.iguana.grammar.symbol.Plus; +import org.iguana.grammar.symbol.Return; +import org.iguana.grammar.symbol.Star; +import org.iguana.grammar.symbol.Start; +import org.iguana.grammar.symbol.Symbol; +import org.iguana.grammar.symbol.Terminal; +import org.iguana.grammar.symbol.While; +import org.iguana.traversal.ISymbolVisitor; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +public class ReachabilityGraph { + + private final Map> definitions; + + private final Map> reachabilityGraph; + + private final Set layouts; + + public ReachabilityGraph(RuntimeGrammar grammar) { + this.reachabilityGraph = new HashMap<>(); + this.definitions = grammar.getDefinitions(); + + this.layouts = new HashSet<>(); + + if (grammar.getLayout() != null) + layouts.add(grammar.getLayout().getName()); + + grammar.getNonterminals().forEach(n -> reachabilityGraph.put(n, new HashSet<>())); + + layouts.addAll(grammar.getRules().stream() + .filter(r -> r.getLayout() != null) + .map(r -> r.getLayout().getName()) + .collect(Collectors.toSet())); + + calculateReachabilityGraph(); + } + + public Map> getReachabilityGraph() { + return reachabilityGraph; + } + + public Set getReachableNonterminals(Nonterminal nt) { + return reachabilityGraph.get(nt); + } + + /* + * + * Calculate the set of nonterminals that are reachable via the alternates of A. + * In other words, if A is a nonterminal, reachable nonterminals are all the B's such as + * A =>* alpha B gamma. Note that this method does not calculate direct-nullable reachable + * nonterminals. + * + */ + public Map> calculateReachabilityGraph() { + + Visitor visitor = new Visitor(reachabilityGraph, layouts); + + Set nonterminals = definitions.keySet(); + + boolean changed = true; + + while (changed) { + + changed = false; + + for (Nonterminal head : nonterminals) { + + // Skips layout + if (layouts.contains(head.getName())) + continue; + + visitor.setHead(head); + + for (RuntimeRule rule : definitions.get(head)) { + + List alternate = rule.getBody(); + + if (alternate == null) + continue; + + for (Symbol symbol : alternate) + changed |= symbol.accept(visitor); + + } + } + } + + return reachabilityGraph; + } + + private static class Visitor implements ISymbolVisitor { + + private final Map> reachabilityGraph; + private final Set layouts; + + private Nonterminal head; + + Visitor(Map> reachabilityGraph, Set layouts) { + this.reachabilityGraph = reachabilityGraph; + this.layouts = layouts; + } + + public void setHead(Nonterminal head) { + this.head = head; + } + + @Override + public Boolean visit(Align symbol) { + return symbol.getSymbol().accept(this); + } + + @Override + public Boolean visit(Block symbol) { + boolean changed = false; + for (Symbol s : symbol.getSymbols()) + changed |= s.accept(this); + return changed; + } + + @Override + public Boolean visit(Code symbol) { + return symbol.getSymbol().accept(this); + } + + @Override + public Boolean visit(Error error) { + return false; + } + + @Override + public Boolean visit(Conditional symbol) { + return symbol.getSymbol().accept(this); + } + + @Override + public Boolean visit(IfThen symbol) { + return symbol.getThenPart().accept(this); + } + + @Override + public Boolean visit(IfThenElse symbol) { + return symbol.getThenPart().accept(this) || symbol.getElsePart().accept(this); + } + + @Override + public Boolean visit(Ignore symbol) { + return symbol.getSymbol().accept(this); + } + + @Override + public Boolean visit(Nonterminal symbol) { + // Skips layout + if (layouts.contains(symbol.getName())) + return false; + return add(head, symbol, reachabilityGraph); + } + + @Override + public Boolean visit(Offside symbol) { + return symbol.getSymbol().accept(this); + } + + @Override + public Boolean visit(Terminal symbol) { + return false; + } + + @Override + public Boolean visit(While symbol) { + return symbol.getBody().accept(this); + } + + @Override + public Boolean visit(Return symbol) { + return false; + } + + @Override + public Boolean visit(Alt symbol) { + boolean changed = false; + for (Symbol s : symbol.getSymbols()) + changed |= s.accept(this); + return changed; + } + + @Override + public Boolean visit(Opt symbol) { + return symbol.getSymbol().accept(this); + } + + @Override + public Boolean visit(Plus symbol) { + boolean changed = symbol.getSymbol().accept(this); + for (Symbol sep : symbol.getSeparators()) + changed |= sep.accept(this); + return changed; + } + + @Override + public Boolean visit(Group symbol) { + boolean changed = false; + for (Symbol s : symbol.getSymbols()) + changed |= s.accept(this); + return changed; + } + + @Override + public Boolean visit(Star symbol) { + boolean changed = symbol.getSymbol().accept(this); + for (Symbol sep : symbol.getSeparators()) + changed |= sep.accept(this); + return changed; + } + + @Override + public Boolean visit(Start start) { +// return start.getNonterminal().accept(this); + return false; + } + + } + + private static boolean add( + Nonterminal a, + Nonterminal nonterminal, + Map> reachabilityGraph) { + boolean changed = reachabilityGraph.get(a).add(nonterminal); + changed |= reachabilityGraph.get(a).addAll(reachabilityGraph.get(nonterminal)); + return changed; + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/grammar/runtime/AssociativityGroup.java b/benchmarks/src/main/kotlin/org/iguana/grammar/runtime/AssociativityGroup.java new file mode 100644 index 000000000..5458eacc0 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/grammar/runtime/AssociativityGroup.java @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.grammar.runtime; + +import org.iguana.grammar.symbol.Associativity; + +import java.util.HashMap; +import java.util.Map; + +import static org.iguana.utils.string.StringUtil.listToString; + + +public class AssociativityGroup { + + private final Associativity associativity; + + private final PrecedenceLevel precedenceLevel; + + private final Map map; + + private int precedence = -1; + + private int lhs = -1; + + private int rhs = -1; + + public AssociativityGroup(Associativity associativity, PrecedenceLevel precedenceLevel) { + this.associativity = associativity; + this.precedenceLevel = precedenceLevel; + this.map = new HashMap<>(); + this.lhs = precedenceLevel.getLhs(); + } + + public AssociativityGroup( + Associativity associativity, + PrecedenceLevel precedenceLevel, + int lhs, + int rhs, + int precedence) { + this(associativity, precedenceLevel); + this.precedence = precedence; + this.lhs = lhs; + this.rhs = rhs; + } + + public Associativity getAssociativity() { + return associativity; + } + + public int getLhs() { + return lhs; + } + + public int getRhs() { + return rhs; + } + + public int getPrecedence() { + return precedence; + } + + public Map getAssocMap() { + return map; + } + + public AssociativityGroup add(int precedence, Associativity associativity) { + + if (precedence == this.precedence) return this; + + map.put(precedence, associativity); + return this; + } + + public int getPrecedence(RuntimeRule rule) { + if (!(rule.isLeftOrRightRecursive() + || rule.isILeftOrRightRecursive())) + return -1; + + if (rule.getAssociativity() == associativity) { + if (precedence != -1) + return precedence; + else { + precedence = precedenceLevel.getPrecedence(rule); + return precedence; + } + } + + if (rule.getAssociativity() == Associativity.UNDEFINED) { + if (precedence != -1) { + + if (rule.isUnary() && rule.isRightRecursive()) + precedenceLevel.setHasPrefixUnaryFromAssociativityGroup(); + if (rule.isUnary() && rule.isLeftRecursive()) + precedenceLevel.setHasPostfixUnaryFromAssociativityGroup(); + + return precedence; + } else { + precedence = precedenceLevel.getPrecedenceFromAssociativityGroup(rule); + return precedence; + } + } + + int precedence = precedenceLevel.getPrecedence(rule); + map.put(precedence, rule.getAssociativity()); + return precedence; + } + + public void done() { + rhs = precedenceLevel.getCurrent(); + } + + @Override + public String toString() { + return associativity.name() + "(" + + lhs + "," + + rhs + "," + + (precedence != -1 ? precedence + (map.keySet().isEmpty() ? "" : ",") : "") + + listToString(map.keySet(), ",") + ")"; + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/grammar/runtime/Position.java b/benchmarks/src/main/kotlin/org/iguana/grammar/runtime/Position.java new file mode 100644 index 000000000..25fe3eb51 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/grammar/runtime/Position.java @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.grammar.runtime; + +import java.util.Objects; + +/** + * + * @author Ali Afroozeh + * + */ +public class Position { + + private final RuntimeRule rule; + private final int posInRule; + private final int posInSymbol; + + public Position(RuntimeRule rule, int posInRule) { + this(rule, posInRule, 0); + } + + public Position(RuntimeRule rule, int posInRule, int posInSymbol) { + this.rule = rule; + this.posInRule = posInRule; + this.posInSymbol = posInSymbol; + } + + public RuntimeRule getRule() { + return rule; + } + + public int getPosition() { + return posInRule; + } + + public boolean isFirst() { + return posInRule == 1; + } + + public boolean isLast() { + return posInRule == rule.size(); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (!(obj instanceof Position)) return false; + Position other = (Position) obj; + return rule.equals(other.rule) + && posInRule == other.posInRule + && posInSymbol == other.posInSymbol; + } + + @Override + public int hashCode() { + return Objects.hash(rule, posInRule, posInSymbol); + } + + @Override + public String toString() { + + if (posInSymbol == -1) + return "-"; + + StringBuilder sb = new StringBuilder(); + sb.append(rule.getHead()).append(" ::= "); + + if (rule.size() == 0) { + sb.append("."); + } else { + int i; + if (posInRule == 0 && posInSymbol == 0) { + sb.append(". "); + } + for (i = 0; i < rule.size(); i++) { + if (i + 1 == posInRule) { + if (posInRule == rule.size()) + sb.append(rule.symbolAt(i).toString().trim() + " ."); + else + sb.append(rule.symbolAt(i).toString().trim() + " . "); + } else { + sb.append(rule.symbolAt(i) + " "); + } + } + + } + + return sb.toString().trim(); + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/grammar/runtime/PrecedenceLevel.java b/benchmarks/src/main/kotlin/org/iguana/grammar/runtime/PrecedenceLevel.java new file mode 100644 index 000000000..c2109c882 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/grammar/runtime/PrecedenceLevel.java @@ -0,0 +1,246 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.grammar.runtime; + +import org.iguana.grammar.symbol.Associativity; + +import java.util.Arrays; + +public class PrecedenceLevel { + + public Integer[] prefixUnaryBelow = new Integer[0]; + public Integer[] postfixUnaryBelow = new Integer[0]; + + private final int lhs; + private int rhs = -1; + + private boolean hasPrefixUnary = false; + private boolean hasPrefixUnaryBelow = false; + + private boolean hasPostfixUnary = false; + private boolean hasPostfixUnaryBelow = false; + + private int undefined = -1; + + private int index; + + private boolean containsAssociativityGroup = false; + private int assoc_lhs = -1; + private int assoc_rhs = -1; + + PrecedenceLevel(int lhs) { + this.lhs = lhs; + this.index = lhs; + } + + public PrecedenceLevel( + int lhs, + int rhs, + int undefined, + boolean hasPrefixUnary, + boolean hasPostfixUnary, + boolean hasPrefixUnaryBelow, + Integer[] prefixUnaryBelow, + boolean hasPostfixUnaryBelow, + Integer[] postfixUnaryBelow) { + this.lhs = lhs; + this.index = lhs; + this.rhs = rhs; + this.undefined = undefined; + this.hasPrefixUnary = hasPrefixUnary; + this.hasPostfixUnary = hasPostfixUnary; + this.prefixUnaryBelow = prefixUnaryBelow; + this.hasPrefixUnaryBelow = hasPrefixUnaryBelow; + this.hasPostfixUnaryBelow = hasPostfixUnaryBelow; + this.postfixUnaryBelow = postfixUnaryBelow; + } + + public static PrecedenceLevel from( + int lhs, + int rhs, + int undefined, + boolean hasPrefixUnary, + boolean hasPostfixUnary, + boolean hasPrefixUnaryBelow, + boolean hasPostfixUnaryBelow) { + PrecedenceLevel level = new PrecedenceLevel(lhs); + level.rhs = rhs; + level.undefined = undefined; + level.hasPrefixUnary = hasPrefixUnary; + level.hasPostfixUnary = hasPostfixUnary; + level.hasPrefixUnaryBelow = hasPrefixUnaryBelow; + level.hasPostfixUnaryBelow = hasPostfixUnaryBelow; + return level; + } + + public static PrecedenceLevel from( + int lhs, + int rhs, + int undefined, + boolean hasPrefixUnary, + boolean hasPostfixUnary, + boolean hasPrefixUnaryBelow, + Integer[] prefixUnaryBelow, + boolean hasPostfixUnaryBelow, + Integer[] postfixUnaryBelow) { + PrecedenceLevel level = new PrecedenceLevel(lhs); + level.rhs = rhs; + level.undefined = undefined; + level.hasPrefixUnary = hasPrefixUnary; + level.hasPostfixUnary = hasPostfixUnary; + level.hasPrefixUnaryBelow = hasPrefixUnaryBelow; + level.prefixUnaryBelow = prefixUnaryBelow; + level.hasPostfixUnaryBelow = hasPostfixUnaryBelow; + level.postfixUnaryBelow = postfixUnaryBelow; + return level; + } + + public static PrecedenceLevel getFirst() { + return new PrecedenceLevel(1); + } + + public static PrecedenceLevel getFirstAndDone() { + PrecedenceLevel level = new PrecedenceLevel(1); + level.rhs = level.lhs; + return level; + } + + public PrecedenceLevel getNext() { + + this.done(); + + PrecedenceLevel next = new PrecedenceLevel(index); + + next.hasPrefixUnaryBelow = hasPrefixUnary || hasPrefixUnaryBelow; + next.hasPostfixUnaryBelow = hasPostfixUnary || hasPostfixUnaryBelow; + + next.prefixUnaryBelow = Arrays.copyOf(prefixUnaryBelow, (hasPrefixUnary ? 1 : 0) + prefixUnaryBelow.length); + next.postfixUnaryBelow = Arrays.copyOf(postfixUnaryBelow, (hasPostfixUnary ? 1 : 0) + postfixUnaryBelow.length); + + if (hasPrefixUnary) + next.prefixUnaryBelow[next.prefixUnaryBelow.length - 1] = rhs; + + if (hasPostfixUnary) + next.postfixUnaryBelow[next.postfixUnaryBelow.length - 1] = rhs; + + return next; + } + + public int getLhs() { + return lhs; + } + + public int getRhs() { + return rhs; + } + + public boolean hasPrefixUnary() { + return hasPrefixUnary; + } + + public boolean hasPostfixUnary() { + return hasPostfixUnary; + } + + public boolean hasPrefixUnaryBelow() { + return hasPrefixUnaryBelow; + } + + public boolean hasPostfixUnaryBelow() { + return hasPostfixUnaryBelow; + } + + public int getPrecedence(RuntimeRule rule) { + + if (rule.isUnary() && rule.isRightRecursive()) hasPrefixUnary = true; + if (rule.isUnary() && rule.isLeftRecursive()) hasPostfixUnary = true; + + if (!(rule.isLeftOrRightRecursive() + || rule.isILeftOrRightRecursive())) return -1; + else if (rule.getAssociativity() == Associativity.UNDEFINED) { + if (undefined == -1) + undefined = index++; + return undefined; + } else + return index++; + } + + int getPrecedenceFromAssociativityGroup(RuntimeRule rule) { + if (rule.isUnary() && rule.isRightRecursive()) hasPrefixUnary = true; + if (rule.isUnary() && rule.isLeftRecursive()) hasPostfixUnary = true; + + if (!(rule.isLeftOrRightRecursive() + || rule.isILeftOrRightRecursive())) return -1; + else return index++; + } + + void setHasPrefixUnaryFromAssociativityGroup() { + this.hasPrefixUnary = true; + } + + void setHasPostfixUnaryFromAssociativityGroup() { + this.hasPostfixUnary = true; + } + + public void setUndefinedIfNeeded() { + if (undefined == -1) { + int rhs = index == lhs ? index : index - 1; + if (lhs != rhs && !(containsAssociativityGroup && lhs == assoc_lhs && rhs == assoc_rhs)) + undefined = index++; + } + } + + public boolean isUndefined(int precedence) { + return this.undefined != -1 && this.undefined == precedence; + } + + public int getUndefined() { + if (lhs == 1) + return 0; + return undefined; + } + + public void done() { + rhs = index == lhs ? index : index - 1; + } + + int getCurrent() { + return index == lhs ? index : index - 1; + } + + public void containsAssociativityGroup(int l, int r) { + this.containsAssociativityGroup = true; + this.assoc_lhs = l; + this.assoc_rhs = r; + } + + @Override + public String toString() { + return "PREC(" + lhs + "," + rhs + ")"; + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/grammar/runtime/Recursion.java b/benchmarks/src/main/kotlin/org/iguana/grammar/runtime/Recursion.java new file mode 100644 index 000000000..b3008f875 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/grammar/runtime/Recursion.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.grammar.runtime; + +public enum Recursion { + + LEFT_REC, RIGHT_REC, LEFT_RIGHT_REC, NON_REC, + iLEFT_REC, iRIGHT_REC, iLEFT_RIGHT_REC; + + @Override + public String toString() { + return this.name(); + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/grammar/runtime/RuntimeGrammar.java b/benchmarks/src/main/kotlin/org/iguana/grammar/runtime/RuntimeGrammar.java new file mode 100644 index 000000000..ee8762602 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/grammar/runtime/RuntimeGrammar.java @@ -0,0 +1,403 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.grammar.runtime; + +import org.iguana.datadependent.ast.Expression; +import org.iguana.grammar.exception.GrammarValidationException; +import org.iguana.grammar.exception.NonterminalNotDefinedException; +import org.iguana.grammar.symbol.Nonterminal; +import org.iguana.grammar.symbol.Start; +import org.iguana.grammar.symbol.Symbol; +import org.iguana.regex.RegularExpression; + +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.ObjectOutputStream; +import java.net.URI; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +import static org.iguana.utils.string.StringUtil.listToString; + + +/** + * + * @author Ali Afroozeh + * @author Anastasia Izmaylova + * + */ +public class RuntimeGrammar { + + private final Map> definitions; + + private final Symbol layout; + + private final List rules; + + private final Map regularExpressionDefinitions; + private final Map literals; + + private final Map> ebnfLefts; + private final Map> ebnfRights; + + private final List startSymbols; + + private final Map globals; + + private final String name; + + public RuntimeGrammar(Builder builder) { + this.definitions = builder.definitions; + this.layout = builder.layout; + this.startSymbols = builder.startSymbols; + this.rules = builder.rules; + this.ebnfLefts = builder.ebnfLefts; + this.ebnfRights = builder.ebnfRights; + this.regularExpressionDefinitions = builder.regularExpressionDefinitions; + this.literals = builder.literals; + this.globals = builder.globals; + this.name = builder.name; + } + + public Builder copy() { + return new Builder(this); + } + + public Map> getDefinitions() { + return definitions; + } + + public List getAlternatives(Nonterminal nonterminal) { + return definitions.get(nonterminal); + } + + public Set getNonterminals() { + return definitions.keySet(); + } + + public List getRules() { + return rules; + } + + public Map> getEBNFLefts() { + return this.ebnfLefts; + } + + public Map> getEBNFRights() { + return this.ebnfRights; + } + + public List getStartSymbols() { + return startSymbols; + } + + public int sizeRules() { + int num = 0; + for (Nonterminal head : definitions.keySet()) { + num += definitions.get(head).size(); + } + return num; + } + + public Map getRegularExpressionDefinitions() { + return regularExpressionDefinitions; + } + + public Map getLiterals() { + return literals; + } + + private static Set validate( + List rules, + Map> definitions) { + Set exceptions = new HashSet<>(); + for (RuntimeRule rule : rules) { + if (rule.getBody() != null) { + for (Symbol s : rule.getBody()) { + if (s instanceof Nonterminal && !definitions.containsKey(s)) { + exceptions.add(new NonterminalNotDefinedException((Nonterminal) s)); + } + } + } + } + return exceptions; + } + + public Symbol getLayout() { + return layout; + } + + public Map getGlobals() { + return globals; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + + for (Nonterminal nonterminal : definitions.keySet()) { + sb.append(nonterminal).append(" = "); + List> alternativesList = definitions.get(nonterminal).stream().map(r -> r.getBody()).collect( + Collectors.toList()); + for (List alternatives : alternativesList) { + if (alternatives == null) continue; + sb.append(listToString(alternatives)).append("\n"); + } + } + + return sb.toString(); + } + + public String toStringWithOrderByPrecedence() { + StringBuilder sb = new StringBuilder(); + + for (Nonterminal nonterminal : definitions.keySet()) { + sb.append(nonterminal).append(" ::= "); + + int precedence = -1; + List rules = definitions.get(nonterminal); + + boolean found = true; + while (found) { + found = false; + for (RuntimeRule rule : rules) { + if (rule.getPrecedence() == precedence) { + found = true; + sb.append(listToString(rule.getBody())).append(" {" + rule.getPrecedence() + "}" + "\n"); + } + } + + if (precedence == 0) { + found = true; + precedence++; + } else precedence++; + } + + } + + return sb.toString(); + } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + + private Map> definitions = new LinkedHashMap<>(); + private List rules = new ArrayList<>(); + private String name; + private Symbol layout; + private List startSymbols; + private Map regularExpressionDefinitions; + private Map literals; + + private Map> ebnfLefts = new HashMap<>(); + private Map> ebnfRights = new HashMap<>(); + private Map globals = new HashMap<>(); + + public Builder() { } + + public Builder(RuntimeGrammar grammar) { + definitions = new HashMap<>(grammar.definitions); + rules = new ArrayList<>(grammar.rules); + layout = grammar.layout; + ebnfLefts = new HashMap<>(grammar.ebnfLefts); + ebnfRights = new HashMap<>(grammar.ebnfRights); + startSymbols = new ArrayList<>(grammar.startSymbols); + regularExpressionDefinitions = new HashMap<>(grammar.getRegularExpressionDefinitions()); + globals = new HashMap<>(grammar.globals); + name = grammar.name; + } + + public RuntimeGrammar build() { + if (definitions.isEmpty()) { + for (RuntimeRule rule : rules) { + definitions.computeIfAbsent(rule.getHead(), k -> new ArrayList<>()).add(rule); + } + } + Set exceptions = validate(rules, definitions); + + if (!exceptions.isEmpty()) { + throw new GrammarValidationException(exceptions); + } + + if (regularExpressionDefinitions == null) + throw new RuntimeException("regularExpressionDefinitions is null"); + + return new RuntimeGrammar(this); + } + + public Builder addRule(RuntimeRule rule) { + List rules = definitions.get(rule.getHead()); + if (rules == null) { + rules = new ArrayList<>(); + definitions.put(rule.getHead(), rules); + } + rules.add(rule); + this.rules.add(rule); + return this; + } + + public Builder addRules(Iterable rules) { + rules.forEach(r -> addRule(r)); + return this; + } + + public Builder addRules(RuntimeRule... rules) { + addRules(Arrays.asList(rules)); + return this; + } + + public Builder setLayout(Symbol layout) { + this.layout = layout; + return this; + } + + public Builder setName(String name) { + this.name = name; + return this; + } + + public Builder setStartSymbols(List startSymbols) { + this.startSymbols = startSymbols; + return this; + } + + public Builder addStartSymbol(Start startSymbol) { + this.startSymbols.add(startSymbol); + return this; + } + + public Builder addEBNFl(Map> ebnfLefts) { + this.ebnfLefts.putAll(ebnfLefts); + return this; + } + + public Builder addEBNFl(String ebnf, Set lefts) { + this.ebnfLefts.put(ebnf, lefts); + return this; + } + + public Builder addEBNFr(Map> ebnfRights) { + this.ebnfRights.putAll(ebnfRights); + return this; + } + + public Builder addEBNFr(String ebnf, Set rights) { + this.ebnfRights.put(ebnf, rights); + return this; + } + + public Builder setEbnfLefts(Map> ebnfLefts) { + this.ebnfLefts = ebnfLefts; + return this; + } + + public Builder setEbnfRights(Map> ebnfRights) { + this.ebnfRights = ebnfRights; + return this; + } + + public Builder setRegularExpressionDefinitions(Map regularExpressionDefinitions) { + this.regularExpressionDefinitions = regularExpressionDefinitions; + return this; + } + + public Builder setLiterals(Map literals) { + this.literals = literals; + return this; + } + + public Builder setGlobals(Map globals) { + this.globals = globals; + return this; + } + } + + public void save(URI uri) { + save(new File(uri)); + } + + public void save(File file) { + if (!file.exists()) { + try { + file.createNewFile(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + try (ObjectOutputStream out = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(file)))) { + out.writeObject(this); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + + if (!(obj instanceof RuntimeGrammar)) + return false; + + RuntimeGrammar other = (RuntimeGrammar) obj; + + return definitions.equals(other.definitions); + } + + @Override + public int hashCode() { + return definitions.hashCode(); + } + + /* + * Returns the size of this grammar, which is equal to the number of nonterminals + + * number of terminals + grammar slots. + */ + public int size() { + int heads = definitions.size(); + int bodySymbols = definitions.values().stream() + .flatMap(l -> l.stream()) + .filter(r -> r.getBody() != null) + .mapToInt(r -> r.size()) + .sum(); + return heads + bodySymbols; + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/grammar/runtime/RuntimeRule.java b/benchmarks/src/main/kotlin/org/iguana/grammar/runtime/RuntimeRule.java new file mode 100644 index 000000000..961e7f101 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/grammar/runtime/RuntimeRule.java @@ -0,0 +1,484 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.grammar.runtime; + +import org.iguana.grammar.slot.NonterminalNodeType; +import org.iguana.grammar.symbol.Associativity; +import org.iguana.grammar.symbol.LayoutStrategy; +import org.iguana.grammar.symbol.Nonterminal; +import org.iguana.grammar.symbol.Symbol; +import org.iguana.utils.collections.hash.MurmurHash3; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +/** + * + * @author Ali Afroozeh + * @author Anastasia Izmaylova + * + */ +public class RuntimeRule { + + private final Nonterminal head; + + private final List body; + + private final Symbol layout; + + private final LayoutStrategy layoutStrategy; + + private final Recursion recursion; + private final Recursion irecursion; + + private final String leftEnd; + private final String rightEnd; + + private final Set leftEnds; + private final Set rightEnds; + + private final Associativity associativity; + private final AssociativityGroup associativityGroup; + + private final int precedence; + private final PrecedenceLevel precedenceLevel; + + private final String label; + + private final Map attributes; + + private final Symbol definition; // Used for converted EBNF nodes + + public RuntimeRule(Builder builder) { + this.body = builder.body; + this.head = builder.head; + this.layout = builder.layout; + this.layoutStrategy = builder.layoutStrategy; + this.recursion = builder.recursion; + this.irecursion = builder.irecursion; + this.leftEnd = builder.leftEnd; + this.rightEnd = builder.rightEnd; + this.leftEnds = builder.leftEnds; + this.rightEnds = builder.rightEnds; + this.associativity = builder.associativity; + this.associativityGroup = builder.associativityGroup; + this.precedence = builder.precedence; + this.precedenceLevel = builder.precedenceLevel; + this.label = builder.label; + this.attributes = builder.attributes; + this.definition = builder.definition; + } + + public Nonterminal getHead() { + return head; + } + + public List getBody() { + return body; + } + + public Symbol getFirstSymbol() { + return symbolAt(0); + } + + public Symbol getLastSymbol() { + return symbolAt(size() - 1); + } + + public int size() { + return body == null ? 0 : body.size(); + } + + public Symbol symbolAt(int i) { + if (i > body.size()) + throw new IllegalArgumentException(i + " cannot be greater than " + body.size()); + + return body.get(i); + } + + public Symbol getLayout() { + return layout; + } + + public LayoutStrategy getLayoutStrategy() { + return layoutStrategy; + } + + public boolean isUnary() { + return recursion == Recursion.LEFT_REC || recursion == Recursion.RIGHT_REC; + } + + public boolean isLeftRecursive() { + return recursion == Recursion.LEFT_RIGHT_REC || recursion == Recursion.LEFT_REC; + } + + public boolean isILeftRecursive() { + return irecursion == Recursion.iLEFT_RIGHT_REC || irecursion == Recursion.iLEFT_REC; + } + + public boolean isRightRecursive() { + return recursion == Recursion.LEFT_RIGHT_REC || recursion == Recursion.RIGHT_REC; + } + + public boolean isIRightRecursive() { + return irecursion == Recursion.iLEFT_RIGHT_REC || irecursion == Recursion.iRIGHT_REC; + } + + public boolean isLeftOrRightRecursive() { + return recursion == Recursion.LEFT_RIGHT_REC + || recursion == Recursion.LEFT_REC + || recursion == Recursion.RIGHT_REC; + } + + public boolean isILeftOrRightRecursive() { + return irecursion == Recursion.iLEFT_RIGHT_REC + || irecursion == Recursion.iLEFT_REC + || irecursion == Recursion.iRIGHT_REC; + } + + public Recursion getRecursion() { + return recursion; + } + + public Recursion getIRecursion() { + return irecursion; + } + + public String getLeftEnd() { + return leftEnd; + } + + public String getRightEnd() { + return rightEnd; + } + + public Set getLeftEnds() { + return leftEnds; + } + + public Set getRightEnds() { + return rightEnds; + } + + public Associativity getAssociativity() { + return associativity; + } + + public AssociativityGroup getAssociativityGroup() { + return associativityGroup; + } + + public int getPrecedence() { + return precedence; + } + + public PrecedenceLevel getPrecedenceLevel() { + return precedenceLevel; + } + + public String getLabel() { + return label; + } + + public boolean hasLayout() { + return layout != null; + } + + public Map getAttributes() { + return attributes; + } + + public Symbol getDefinition() { + return definition; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(head).append(" = "); + for (Symbol s : body) { + sb.append(s).append(" "); + } + + if (associativity != Associativity.UNDEFINED && precedence != 0) { + sb.append(" {").append(associativity.name()); + if (precedence != 0) sb.append(", " + precedence); + if (recursion != Recursion.NON_REC) sb.append(", " + recursion); + sb.append(" }"); + } + + if (associativityGroup != null) sb.append(associativityGroup + " " ); + if (precedenceLevel != null) sb.append(precedenceLevel + " "); + if (label != null) sb.append(label); + + return sb.toString(); + } + + public boolean equals(Object obj) { + if (this == obj) return true; + if (!(obj instanceof RuntimeRule)) return false; + + RuntimeRule other = (RuntimeRule) obj; + + return head.equals(other.head) && Objects.equals(body, other.body); + } + + @Override + public int hashCode() { + return MurmurHash3.fn().apply(head, body); + } + + public Position getPosition(int i) { + if (i < 0) + throw new IllegalArgumentException("i cannot be less than zero."); + + if (i > size()) + throw new IllegalArgumentException("i cannot be greater than the size."); + + return new Position(this, i); + } + + public Position getPosition(int i, int j) { + if (i < 0) + throw new IllegalArgumentException("i cannot be less than zero."); + + if (i > size()) + throw new IllegalArgumentException("i cannot be greater than the size."); + + return new Position(this, i, j); + } + + public Builder copy() { + return new Builder(this); + } + + public Builder copyBuilderButWithHead(Nonterminal nonterminal) { + Builder builder = new Builder(this); + builder.head = nonterminal; + return builder; + } + + public static Builder withHead(Nonterminal nonterminal) { + return new Builder(nonterminal); + } + + public String head() { + return head.getName(); + } + + public String label() { + return label; + } + + + public int position() { + return size(); + } + + public boolean isLayout() { + return head.getNodeType() == NonterminalNodeType.Layout; + } + + public static class Builder { + + private Nonterminal head; + private List body = new ArrayList<>(); + private LayoutStrategy layoutStrategy = LayoutStrategy.INHERITED; + private Symbol layout; + + private Recursion recursion = Recursion.NON_REC; + private Recursion irecursion = Recursion.NON_REC; + + private String leftEnd = ""; + private String rightEnd = ""; + private Set leftEnds = new HashSet<>(); + private Set rightEnds = new HashSet<>(); + + private Associativity associativity = Associativity.UNDEFINED; + private AssociativityGroup associativityGroup; + + private int precedence; + private PrecedenceLevel precedenceLevel; + + private String label; + + private Map attributes = new HashMap<>(); + private Symbol definition; + + public Builder(Nonterminal head) { + this.head = head; + } + + public Builder() {} + + public Builder(RuntimeRule rule) { + this.head = rule.head; + this.body = rule.body; + this.layoutStrategy = rule.layoutStrategy; + this.layout = rule.layout; + this.recursion = rule.recursion; + this.irecursion = rule.irecursion; + this.leftEnd = rule.leftEnd; + this.rightEnd = rule.rightEnd; + this.leftEnds = rule.leftEnds; + this.rightEnds = rule.rightEnds; + this.associativity = rule.associativity; + + this.associativityGroup = rule.associativityGroup; + this.precedence = rule.precedence; + this.precedenceLevel = rule.precedenceLevel; + + this.label = rule.label; + + this.attributes = rule.attributes; + this.definition = rule.definition; + } + + public Builder addSymbol(Symbol symbol) { + body.add(symbol); + return this; + } + + public Builder addSymbols(Symbol... symbols) { + body.addAll(Arrays.asList(symbols)); + return this; + } + + public Builder addSymbols(List symbols) { + body.addAll(symbols); + return this; + } + + public Builder setSymbols(List symbols) { + body = symbols; + return this; + } + + public Builder setLayoutStrategy(LayoutStrategy layoutStrategy) { + this.layoutStrategy = layoutStrategy; + return this; + } + + public Builder setLayout(Symbol layout) { + this.layout = layout; + return this; + } + + public Builder setRecursion(Recursion recursion) { + this.recursion = recursion; + return this; + } + + public Builder setiRecursion(Recursion irecursion) { + this.irecursion = irecursion; + return this; + } + + public Builder setLeftEnd(String end) { + this.leftEnd = end; + return this; + } + + public Builder setRightEnd(String end) { + this.rightEnd = end; + return this; + } + + public Builder setLeftEnds(Set leftEnds) { + if (leftEnds != null) + this.leftEnds = leftEnds; + return this; + } + + public Builder setRightEnds(Set rightEnds) { + if (rightEnds != null) + this.rightEnds = rightEnds; + return this; + } + + public Builder setAssociativity(Associativity associativity) { + this.associativity = associativity; + return this; + } + + public Builder setAssociativityGroup(AssociativityGroup associativityGroup) { + this.associativityGroup = associativityGroup; + return this; + } + + public Builder setPrecedence(int precedence) { + this.precedence = precedence; + return this; + } + + public Builder setPrecedenceLevel(PrecedenceLevel precedenceLevel) { + this.precedenceLevel = precedenceLevel; + return this; + } + + public Builder setLabel(String label) { + this.label = label; + return this; + } + + public Builder setAttributes(Map attributes) { + this.attributes = attributes; + return this; + } + + public Builder addAttribute(String key, Object value) { + this.attributes.put(key, value); + return this; + } + + public Builder addAttributes(Map attributes) { + this.attributes.putAll(attributes); + return this; + } + + public Builder setDefinition(Symbol definition) { + this.definition = definition; + return this; + } + + public RuntimeRule build() { + if (body == null) body = Collections.emptyList(); + if (body.stream().anyMatch(Objects::isNull)) + throw new IllegalArgumentException("symbols cannot be null: " + body); + return new RuntimeRule(this); + } + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/grammar/slot/AbstractTransition.java b/benchmarks/src/main/kotlin/org/iguana/grammar/slot/AbstractTransition.java new file mode 100644 index 000000000..b3260726b --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/grammar/slot/AbstractTransition.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.grammar.slot; + + +public abstract class AbstractTransition implements Transition { + + protected final BodyGrammarSlot dest; + + protected final BodyGrammarSlot origin; + + public AbstractTransition(BodyGrammarSlot origin, BodyGrammarSlot dest) { + this.origin = origin; + this.dest = dest; + if (origin == dest) { + throw new IllegalArgumentException("Origin " + origin + " and Destination " + dest + " are equal"); + } + } + + @Override + public BodyGrammarSlot destination() { + return dest; + } + + @Override + public BodyGrammarSlot origin() { + return origin; + } + + @Override + public String toString() { + return origin + " ---> " + dest; + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/grammar/slot/BodyGrammarSlot.java b/benchmarks/src/main/kotlin/org/iguana/grammar/slot/BodyGrammarSlot.java new file mode 100644 index 000000000..0ee4c47bd --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/grammar/slot/BodyGrammarSlot.java @@ -0,0 +1,269 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.grammar.slot; + +import org.iguana.datadependent.env.Environment; +import org.iguana.grammar.condition.Conditions; +import org.iguana.grammar.runtime.Position; +import org.iguana.grammar.runtime.RuntimeRule; +import org.iguana.grammar.slot.lookahead.FollowTest; +import org.iguana.gss.GSSNode; +import org.iguana.parser.IguanaRuntime; +import org.iguana.result.Result; +import org.iguana.utils.collections.Keys; +import org.iguana.utils.collections.OpenAddressingHashMap; +import org.iguana.utils.collections.key.Key; +import org.iguana.utils.input.Input; + +import java.util.Map; +import java.util.Set; + +public class BodyGrammarSlot implements GrammarSlot { + + protected final Position position; + + private Map intermediateNodes; + + private final Conditions conditions; + + private final String label; + + private final int i1; + + private final String variable; + + private final int i2; + + private final Set state; + + private final FollowTest followTest; + + private Transition outTransition; + + private Transition inTransition; + + public BodyGrammarSlot( + Position position, + String label, + String variable, + Set state, + Conditions conditions, + FollowTest followTest) { + this(position, label, -1, variable, -1, state, conditions, followTest); + } + + public BodyGrammarSlot( + Position position, + String label, + int i1, + String variable, + int i2, + Set state, + Conditions conditions, + FollowTest followTest) { + this.position = position; + this.conditions = conditions; + this.label = label; + this.i1 = i1; + this.variable = variable; + this.i2 = i2; + this.state = state; + this.followTest = followTest; + } + + @Override + public String toString() { + return position.toString(); + } + + public boolean testFollow(int v) { + return followTest.test(v); + } + + @SuppressWarnings("unchecked") + public T getIntermediateNode( + T leftResult, + int destinationIndex, + T rightResult, + Environment env, + IguanaRuntime runtime) { + if (isFirst()) return rightResult; + + Key key = Keys.from(destinationIndex, rightResult.getRightExtent(), env); + + if (intermediateNodes == null) { + intermediateNodes = new OpenAddressingHashMap<>(); + } + + Object value = intermediateNodes.get(key); + if (value == null) { + T newNode = runtime.getResultOps().merge(null, leftResult, rightResult, this); + intermediateNodes.put(key, newNode); + return newNode; + } + + runtime.getResultOps().merge((T) value, leftResult, rightResult, this); + return null; + } + + public Conditions getConditions() { + return conditions; + } + + @Override + public void reset() { + intermediateNodes = null; + } + + public String getLabel() { + return label; + } + + public String getVariable() { + return variable; + } + + public void execute( + Input input, + GSSNode u, + T result, + Environment env, + IguanaRuntime runtime) { + outTransition.execute(input, u, result, env, runtime); + } + + public boolean requiresBinding() { + return label != null || variable != null || state != null; + } + + public Environment doBinding(Result result, Environment env) { + + if (label != null) { + if (i1 != -1) + env = env._declare(result); + else + env = env._declare(label, result); + } + + if (variable != null && state == null) { + if (i2 != -1) + env = env._declare(result.getValue()); + else + env = env._declare(variable, result.getValue()); + } + + if (variable == null && state != null) { // TODO: support for the array-based environment implementation + if (state.size() == 1) { + String v = state.iterator().next(); + if (!v.equals("_")) { + Object value = result.getValue(); + env = env._declare(v, value); + } + } else { + if (result.getValue() instanceof Object[]) { + Object[] values = (Object[]) result.getValue(); + int i = 0; + for (String v : state) { + if (!v.equals("_")) { + env = env._declare(v, values[i]); + } + i++; + } + } + } + } + + if (variable != null && state != null) { // TODO: support for the array-based environment implementation + Object[] values = (Object[]) result.getValue(); + env = env._declare(variable, values[0]); + + int i = 1; + + for (String v : state) { + if (!v.equals("_")) { + env = env._declare(v, values[i]); + } + i++; + } + } + + return env; + } + + public int getPosition() { + return position.getPosition(); + } + + public RuntimeRule getRule() { + return position.getRule(); + } + + public void setOutTransition(Transition outTransition) { + if (this.outTransition != null) { + throw new RuntimeException("outTransition is already set"); + } + this.outTransition = outTransition; + } + + public void setInTransition(Transition inTransition) { + if (this.inTransition != null) { + throw new RuntimeException("inTransition is already set"); + } + this.inTransition = inTransition; + } + + public Transition getOutTransition() { + return outTransition; + } + + public Transition getInTransition() { + return inTransition; + } + + /* + * Corresponds to a grammar position A ::= B . \alpha + */ + public boolean isFirst() { + return getPosition() == 1; + } + + /* + * Corresponds to a grammar position A ::= . \alpha + */ + public boolean isStart() { + return getPosition() == 0; + } + + public boolean isEnd() { + return false; + } + + public FollowTest getFollowTest() { + return followTest; + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/grammar/slot/CodeTransition.java b/benchmarks/src/main/kotlin/org/iguana/grammar/slot/CodeTransition.java new file mode 100644 index 000000000..22dbd6784 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/grammar/slot/CodeTransition.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.grammar.slot; + +import org.iguana.datadependent.ast.Statement; +import org.iguana.datadependent.env.Environment; +import org.iguana.gss.GSSNode; +import org.iguana.parser.IguanaRuntime; +import org.iguana.result.Result; +import org.iguana.utils.input.Input; + +import static org.iguana.utils.string.StringUtil.listToString; + +public class CodeTransition extends AbstractTransition { + + private final Statement[] statements; + + public CodeTransition(Statement[] statements, BodyGrammarSlot origin, BodyGrammarSlot dest) { + super(origin, dest); + this.statements = statements; + } + + @Override + public void execute( + Input input, + GSSNode u, + T result, + Environment env, + IguanaRuntime runtime) { + runtime.evaluate(statements, env, input); + dest.execute(input, u, result, runtime.getEnvironment(), runtime); + } + + @Override + public String getLabel() { + return listToString(statements, ";"); + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/grammar/slot/ConditionalTransition.java b/benchmarks/src/main/kotlin/org/iguana/grammar/slot/ConditionalTransition.java new file mode 100644 index 000000000..ba97beee3 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/grammar/slot/ConditionalTransition.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.grammar.slot; + +import org.iguana.datadependent.ast.Expression; +import org.iguana.datadependent.env.Environment; +import org.iguana.grammar.exception.UnexpectedRuntimeTypeException; +import org.iguana.gss.GSSNode; +import org.iguana.parser.IguanaRuntime; +import org.iguana.result.Result; +import org.iguana.utils.input.Input; + +public class ConditionalTransition extends AbstractTransition { + + private final Expression condition; + + private final BodyGrammarSlot ifFalse; + + public ConditionalTransition(Expression condition, BodyGrammarSlot origin, BodyGrammarSlot dest) { + this(condition, origin, dest, null); + } + + private ConditionalTransition( + Expression condition, + BodyGrammarSlot origin, + BodyGrammarSlot dest, + BodyGrammarSlot ifFalse) { + super(origin, dest); + this.condition = condition; + this.ifFalse = ifFalse; + } + + public BodyGrammarSlot ifFalseDestination() { + return ifFalse; + } + + @Override + public String getLabel() { + return String.format("[%s]", condition.toString()); + } + + @Override + public void execute( + Input input, + GSSNode u, + T result, + Environment env, + IguanaRuntime runtime) { + Object value = runtime.evaluate(condition, env, input); + + if (!(value instanceof Boolean)) { + throw new UnexpectedRuntimeTypeException(condition); + } + + boolean isTrue = (Boolean) value; + + if (isTrue) + dest.execute(input, u, result, env, runtime); + else if (ifFalse != null) + ifFalse.execute(input, u, result, env, runtime); + // TODO: logging + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/grammar/slot/EndGrammarSlot.java b/benchmarks/src/main/kotlin/org/iguana/grammar/slot/EndGrammarSlot.java new file mode 100644 index 000000000..39091cff3 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/grammar/slot/EndGrammarSlot.java @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.grammar.slot; + +import org.iguana.datadependent.env.Environment; +import org.iguana.grammar.condition.Conditions; +import org.iguana.grammar.runtime.Position; +import org.iguana.grammar.slot.lookahead.FollowTest; +import org.iguana.gss.GSSNode; +import org.iguana.parser.IguanaRuntime; +import org.iguana.result.Result; +import org.iguana.utils.input.Input; + +import java.util.Set; + +public class EndGrammarSlot extends BodyGrammarSlot { + + protected final NonterminalGrammarSlot nonterminal; + + public EndGrammarSlot( + Position position, + NonterminalGrammarSlot nonterminal, + String label, + String variable, + Set state, + Conditions conditions, + FollowTest followTest) { + this(position, nonterminal, label, -1, variable, -1, state, conditions, followTest); + } + + public EndGrammarSlot( + Position position, + NonterminalGrammarSlot nonterminal, + String label, + int i1, + String variable, + int i2, + Set state, + Conditions conditions, + FollowTest followTest) { + super(position, label, i1, variable, i2, state, conditions, followTest); + this.nonterminal = nonterminal; + } + + public NonterminalGrammarSlot getNonterminal() { + return nonterminal; + } + + @Override + public boolean isEnd() { + return true; + } + + public Object getObject() { + return null; + } + + @Override + public void execute( + Input input, + GSSNode u, + T result, + Environment env, + IguanaRuntime runtime) { + execute(input, u, result, result.getValue(), runtime); + } + + public void execute( + Input input, + GSSNode u, + T result, + Object value, + IguanaRuntime runtime) { + int rightExtent = result.isDummy() ? u.getInputIndex() : result.getRightExtent(); + int nextChar = input.charAt(rightExtent); + FollowTest followTest = nonterminal.getFollowTest(); + if (followTest.test(nextChar)) { + u.pop(input, this, result, value, runtime); + } else { + runtime.recordParseError(rightExtent, input, this, u, + "Expected " + followTest + " but was " + (char) nextChar); + } + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/grammar/slot/EpsilonGrammarSlot.java b/benchmarks/src/main/kotlin/org/iguana/grammar/slot/EpsilonGrammarSlot.java new file mode 100644 index 000000000..88d8a7a61 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/grammar/slot/EpsilonGrammarSlot.java @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.grammar.slot; + +import org.iguana.datadependent.env.Environment; +import org.iguana.grammar.condition.Conditions; +import org.iguana.grammar.runtime.Position; +import org.iguana.grammar.slot.lookahead.FollowTest; +import org.iguana.gss.GSSNode; +import org.iguana.parser.IguanaRuntime; +import org.iguana.result.Result; +import org.iguana.utils.input.Input; + +public class EpsilonGrammarSlot extends EndGrammarSlot { + + private final TerminalGrammarSlot epsilonSlot; + + public EpsilonGrammarSlot( + Position position, + NonterminalGrammarSlot nonterminal, + TerminalGrammarSlot epsilonSlot, + Conditions conditions) { + super(position, nonterminal, null, null, null, conditions, FollowTest.DEFAULT); + + this.epsilonSlot = epsilonSlot; + } + + @Override + public void execute( + Input input, + GSSNode u, + T result, + Environment env, + IguanaRuntime runtime) { + execute(input, u, result, (Object) null, runtime); + } + + @Override + public void execute( + Input input, + GSSNode u, + T result, + Object value, + IguanaRuntime runtime) { + int i = result.isDummy() ? u.getInputIndex() : result.getRightExtent(); + + int nextChar = input.charAt(i); + FollowTest followTest = nonterminal.getFollowTest(); + if (followTest.test(nextChar)) { + u.pop(input, this, epsilonSlot.getResult(input, i, runtime), value, runtime); + } else { + runtime.recordParseError(i, input, this, u, "Expected " + followTest + " but was " + (char) nextChar); + } + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/grammar/slot/EpsilonTransition.java b/benchmarks/src/main/kotlin/org/iguana/grammar/slot/EpsilonTransition.java new file mode 100644 index 000000000..c679cb889 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/grammar/slot/EpsilonTransition.java @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.grammar.slot; + + +import org.iguana.datadependent.ast.AST; +import org.iguana.datadependent.ast.Expression; +import org.iguana.datadependent.env.Environment; +import org.iguana.grammar.condition.Conditions; +import org.iguana.grammar.exception.UnexpectedRuntimeTypeException; +import org.iguana.gss.GSSNode; +import org.iguana.parser.IguanaRuntime; +import org.iguana.result.Result; +import org.iguana.util.Tuple; +import org.iguana.utils.input.Input; + +import java.util.Objects; + +public class EpsilonTransition extends AbstractTransition { + + private final Type type; + private final String label; + private final Conditions conditions; + + public EpsilonTransition(Conditions conditions, BodyGrammarSlot origin, BodyGrammarSlot dest) { + this(Type.DUMMY, conditions, origin, dest); + } + + private EpsilonTransition(Type type, Conditions conditions, BodyGrammarSlot origin, BodyGrammarSlot dest) { + super(origin, dest); + this.type = type; + this.label = null; + this.conditions = conditions; + } + + public EpsilonTransition( + Type type, + String label, + Conditions conditions, + BodyGrammarSlot origin, + BodyGrammarSlot dest) { + super(origin, dest); + if (Objects.equals(origin.position, dest.position)) { + throw new IllegalArgumentException("Origin and destination positions must be the same for epsilon " + + "transitions: " + origin.position + " is not equal to " + dest.position); + } + assert label != null && (type == Type.DECLARE_LABEL || type == Type.STORE_LABEL); + + this.type = type; + this.label = label; + this.conditions = conditions; + } + + @Override + public String getLabel() { + switch (type) { + case CLEAR_LABEL: + return "?"; + case CLOSE: + return "} " + conditions; + case DECLARE_LABEL: + return label + ".lExt " + conditions; + case DUMMY: + return conditions.toString().equals("") ? String.valueOf('\u2205') : conditions.toString(); + case OPEN: + return conditions + " {"; + case STORE_LABEL: + return label + ".rExt " + conditions; + } + throw new RuntimeException("Unknown type of an epsilon transition."); + } + + @Override + public void execute( + Input input, + GSSNode u, + T result, + Environment env, + IguanaRuntime runtime) { + int i = result.isDummy() ? u.getInputIndex() : result.getRightExtent(); + + runtime.setEnvironment(env); + + switch (type) { + + case DUMMY: + if (conditions.execute(input, origin, u, i, runtime.getEvaluatorContext(), runtime)) + return; + break; + + case CLEAR_LABEL: // TODO: Decide if this case is needed + break; + + case OPEN: + if (conditions.execute(input, origin, u, i, runtime.getEvaluatorContext(), runtime)) + return; + runtime.getEvaluatorContext().pushEnvironment(); + break; + + case CLOSE: + runtime.getEvaluatorContext().popEnvironment(); + if (conditions.execute(input, origin, u, i, runtime.getEvaluatorContext(), runtime)) + return; + break; + + case DECLARE_LABEL: + runtime.getEvaluatorContext().declareVariable(label, Tuple.of(i, -1)); + runtime.getEvaluatorContext().declareVariable( + String.format(Expression.LeftExtent.format, label), Tuple.of(i, -1)); + + if (conditions.execute(input, origin, u, i, runtime.getEvaluatorContext(), runtime)) + return; + break; + + case STORE_LABEL: + + Object value = runtime.getEvaluatorContext().lookupVariable(label); + + Integer lhs; + if (!(value instanceof Tuple)) { + lhs = (Integer) ((Tuple) value).getFirst(); + } else { + throw new UnexpectedRuntimeTypeException(AST.var(label)); + } + + runtime.getEvaluatorContext().storeVariable(label, Tuple.of(lhs, i)); + + if (conditions.execute(input, origin, u, i, runtime.getEvaluatorContext(), runtime)) + return; + break; + } + + dest.execute(input, u, result, runtime.getEnvironment(), runtime); + } + + public enum Type { + DUMMY, OPEN, CLOSE, DECLARE_LABEL, STORE_LABEL, CLEAR_LABEL + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/grammar/slot/ErrorTransition.java b/benchmarks/src/main/kotlin/org/iguana/grammar/slot/ErrorTransition.java new file mode 100644 index 000000000..aeefd0c9a --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/grammar/slot/ErrorTransition.java @@ -0,0 +1,49 @@ +package org.iguana.grammar.slot; + +import org.iguana.datadependent.env.Environment; +import org.iguana.gss.GSSNode; +import org.iguana.parser.IguanaRuntime; +import org.iguana.result.Result; +import org.iguana.utils.input.Input; + +public class ErrorTransition extends AbstractTransition { + + public ErrorTransition(BodyGrammarSlot origin, BodyGrammarSlot dest) { + super(origin, dest); + } + + @Override + public String getLabel() { + throw new UnsupportedOperationException(); + } + + public void handleError( + Input input, + GSSNode u, + T result, + Environment env, + IguanaRuntime runtime) { + int rightExtent = result.isDummy() ? u.getInputIndex() : result.getRightExtent(); + int i = rightExtent; + while (i < input.length() && !dest.testFollow(input.charAt(i))) { + i++; + } + if (i < input.length()) { + T cr = runtime.getResultOps().error(this.dest, rightExtent, i); + T n = dest.isFirst() ? cr : runtime.getResultOps().merge(null, result, cr, dest); + dest.execute(input, u, n, env, runtime); + } else { + System.out.println("Warning: could not recover from the parse error: " + origin); + } + } + + @Override + public void execute( + Input input, + GSSNode u, + T result, + Environment env, + IguanaRuntime runtime) { + dest.execute(input, u, result, env, runtime); + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/grammar/slot/GrammarSlot.java b/benchmarks/src/main/kotlin/org/iguana/grammar/slot/GrammarSlot.java new file mode 100644 index 000000000..1d7190a30 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/grammar/slot/GrammarSlot.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.grammar.slot; + +/** + * A GrammarSlot is a position immediately before or after + * a symbol in the body of a production rule. + * Grammar slots, similar to LR items, are represented by + * For example, in the rule X ::= alpha . beta, the grammar + * slot, denoted by ., is after + * alpha and before beta, where alpha are a list of grammar symbols. + * + * @author Ali Afroozeh + * + */ +public interface GrammarSlot { + + void reset(); + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/grammar/slot/NonterminalGrammarSlot.java b/benchmarks/src/main/kotlin/org/iguana/grammar/slot/NonterminalGrammarSlot.java new file mode 100644 index 000000000..091ef328f --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/grammar/slot/NonterminalGrammarSlot.java @@ -0,0 +1,225 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.grammar.slot; + +import org.iguana.datadependent.ast.Expression; +import org.iguana.datadependent.env.Environment; +import org.iguana.grammar.slot.lookahead.FollowTest; +import org.iguana.grammar.symbol.Nonterminal; +import org.iguana.gss.DefaultGSSNode; +import org.iguana.gss.GSSEdge; +import org.iguana.gss.GSSNode; +import org.iguana.parser.IguanaRuntime; +import org.iguana.result.Result; +import org.iguana.util.Configuration.EnvironmentImpl; +import org.iguana.util.ParserLogger; +import org.iguana.utils.collections.Keys; +import org.iguana.utils.collections.OpenAddressingHashMap; +import org.iguana.utils.collections.key.Key; +import org.iguana.utils.collections.primitive.IntHashMap; +import org.iguana.utils.collections.primitive.OpenAddressingIntHashMap; +import org.iguana.utils.collections.rangemap.RangeMap; +import org.iguana.utils.input.Input; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import static java.util.Collections.emptyList; + + +public class NonterminalGrammarSlot implements GrammarSlot { + + private final Nonterminal nonterminal; + + private final List firstSlots; + + private Map> gssNodes; + + private IntHashMap> intGSSNodes; + + private RangeMap lookAheadTest; + + private FollowTest followTest; + + public NonterminalGrammarSlot(Nonterminal nonterminal) { + this.nonterminal = nonterminal; + this.firstSlots = new ArrayList<>(); + } + + public void addFirstSlot(BodyGrammarSlot slot) { + firstSlots.add(slot); + } + + public List getFirstSlots() { + return firstSlots; + } + + private List getFirstSlots(int v) { + return lookAheadTest.get(v); + } + + public void setLookAheadTest(RangeMap lookAheadTest) { + this.lookAheadTest = lookAheadTest; + } + + public void setFollowTest(FollowTest followTest) { + this.followTest = followTest; + } + + public FollowTest getFollowTest() { + return followTest; + } + + public Nonterminal getNonterminal() { + return nonterminal; + } + + public NonterminalNodeType getNodeType() { + return nonterminal.getNodeType(); + } + + public List getParameters() { + return nonterminal.getParameters(); + } + + public Expression[] getArguments() { + return nonterminal.getArguments(); + } + + public int countGSSNodes() { + if (gssNodes == null) { + return 0; + } + return gssNodes.size(); + } + + @Override + public String toString() { + return nonterminal.toString(); + } + + public Iterable> getGSSNodes() { + if (gssNodes == null) { + return emptyList(); + } + return gssNodes.values(); + } + + @Override + public void reset() { + gssNodes = null; + intGSSNodes = null; + } + + @SuppressWarnings("unchecked") + public void create( + Input input, + BodyGrammarSlot returnSlot, + GSSNode u, + T result, + Expression[] arguments, + Environment env, + IguanaRuntime runtime) { + int i = result.isDummy() ? u.getInputIndex() : result.getRightExtent(); + + Key key = null; + Object[] data = null; + + if (arguments != null) { + data = runtime.evaluate(arguments, env, input); + key = Keys.from(i, data); + } + + GSSNode gssNode = null; + + if (arguments == null) { + if (intGSSNodes == null) { + intGSSNodes = new OpenAddressingIntHashMap<>(); + } else { + gssNode = (GSSNode) intGSSNodes.get(i); + } + } else { + if (gssNodes == null) { + gssNodes = new OpenAddressingHashMap<>(); + } else { + gssNode = (GSSNode) gssNodes.get(key); + } + } + + if (gssNode == null) { + + List firstSlots = getFirstSlots(input.charAt(i)); + if (firstSlots == null || firstSlots.isEmpty()) { + return; + } + + GSSEdge gssEdge = runtime.createGSSEdge(returnSlot, result, u, env); + gssNode = new DefaultGSSNode<>(gssEdge, i); + + ParserLogger.getInstance().gssNodeAdded(gssNode, data); + ParserLogger.getInstance().gssEdgeAdded(gssEdge); + + Environment newEnv = runtime.getEnvironment(); + + if (data != null) { + if (runtime.getConfiguration().getEnvImpl() == EnvironmentImpl.ARRAY + || runtime.getConfiguration().getEnvImpl() == EnvironmentImpl.INT_ARRAY) + newEnv = runtime.getEmptyEnvironment().declare(data); + else + newEnv = runtime.getEmptyEnvironment().declare( + nonterminal.getParameters().toArray(new String[]{}), data); + } + + for (int j = 0; j < firstSlots.size(); j++) { + BodyGrammarSlot slot = firstSlots.get(j); + runtime.setEnvironment(newEnv); + + if (slot.getLabel() != null) + runtime.getEvaluatorContext().declareVariable( + String.format(Expression.LeftExtent.format, slot.getLabel()), i); + + int inputIndex = result.isDummy() ? gssNode.getInputIndex() : result.getRightExtent(); + if (!slot.getConditions().execute(input, returnSlot, gssNode, inputIndex, runtime.getEvaluatorContext(), + runtime)) { + runtime.scheduleDescriptor(slot, gssNode, runtime.getResultOps().dummy(), runtime.getEnvironment()); + } + + } + + if (arguments == null) { + intGSSNodes.put(i, gssNode); + } else { + gssNodes.put(key, gssNode); + } + } else { + gssNode.addGSSEdge(input, returnSlot, i, u, result, env, runtime); + } + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/grammar/slot/NonterminalNodeType.java b/benchmarks/src/main/kotlin/org/iguana/grammar/slot/NonterminalNodeType.java new file mode 100644 index 000000000..ae51e6369 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/grammar/slot/NonterminalNodeType.java @@ -0,0 +1,12 @@ +package org.iguana.grammar.slot; + +public enum NonterminalNodeType { + Basic, + Layout, + Start, + Star, + Plus, + Opt, + Seq, + Alt, +} diff --git a/benchmarks/src/main/kotlin/org/iguana/grammar/slot/NonterminalTransition.java b/benchmarks/src/main/kotlin/org/iguana/grammar/slot/NonterminalTransition.java new file mode 100644 index 000000000..7ec54ea65 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/grammar/slot/NonterminalTransition.java @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.grammar.slot; + +import org.iguana.datadependent.ast.Expression; +import org.iguana.datadependent.env.Environment; +import org.iguana.grammar.condition.Conditions; +import org.iguana.gss.GSSNode; +import org.iguana.parser.IguanaRuntime; +import org.iguana.result.Result; +import org.iguana.utils.input.Input; + +import static org.iguana.utils.string.StringUtil.listToString; + + +public class NonterminalTransition extends AbstractTransition { + + private final NonterminalGrammarSlot nonterminal; + + private final Conditions preConditions; + + private final Expression[] arguments; + + public NonterminalTransition( + NonterminalGrammarSlot nonterminal, + BodyGrammarSlot origin, + BodyGrammarSlot dest, + Expression[] arguments, + Conditions preConditions) { + super(origin, dest); + this.nonterminal = nonterminal; + this.arguments = arguments; + this.preConditions = preConditions; + } + + public NonterminalGrammarSlot getSlot() { + return nonterminal; + } + + @Override + public String getLabel() { + return (dest.getVariable() != null ? dest.getVariable() + "=" : "") + + (dest.getLabel() != null ? dest.getLabel() + ":" : "") + + (arguments != null ? String.format("%s(%s)", getSlot().toString(), listToString(arguments, ",")) + : getSlot().toString()); + } + + @Override + public void execute( + Input input, + GSSNode u, + T result, + Environment env, + IguanaRuntime runtime) { + int i = result.isDummy() ? u.getInputIndex() : result.getRightExtent(); + + if (dest.getLabel() != null) { + env = env._declare(String.format(Expression.LeftExtent.format, dest.getLabel()), i); + } + + runtime.setEnvironment(env); + + if (preConditions.execute(input, origin, u, i, runtime.getEvaluatorContext(), runtime)) + return; + + nonterminal.create(input, dest, u, result, arguments, runtime.getEnvironment(), runtime); + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/grammar/slot/ReturnTransition.java b/benchmarks/src/main/kotlin/org/iguana/grammar/slot/ReturnTransition.java new file mode 100644 index 000000000..9ea1b6f52 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/grammar/slot/ReturnTransition.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.grammar.slot; + +import org.iguana.datadependent.ast.Expression; +import org.iguana.datadependent.env.Environment; +import org.iguana.gss.GSSNode; +import org.iguana.parser.IguanaRuntime; +import org.iguana.result.Result; +import org.iguana.utils.input.Input; + +public class ReturnTransition extends AbstractTransition { + + private final Expression expression; + + public ReturnTransition(Expression expression, BodyGrammarSlot origin, BodyGrammarSlot dest) { + super(origin, dest); + this.expression = expression; + } + + @Override + public String getLabel() { + return expression.toString(); + } + + @Override + public void execute( + Input input, + GSSNode u, + T result, + Environment env, + IguanaRuntime runtime) { + Object value = runtime.evaluate(expression, env, input); + ((EndGrammarSlot) dest).execute(input, u, result, value, runtime); + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/grammar/slot/TerminalGrammarSlot.java b/benchmarks/src/main/kotlin/org/iguana/grammar/slot/TerminalGrammarSlot.java new file mode 100644 index 000000000..e96e19ccb --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/grammar/slot/TerminalGrammarSlot.java @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.grammar.slot; + +import org.iguana.grammar.symbol.Terminal; +import org.iguana.parser.IguanaRuntime; +import org.iguana.regex.matcher.Matcher; +import org.iguana.regex.matcher.MatcherFactory; +import org.iguana.result.Result; +import org.iguana.utils.collections.primitive.IntHashMap; +import org.iguana.utils.collections.primitive.OpenAddressingIntHashMap; +import org.iguana.utils.input.Input; + +public class TerminalGrammarSlot implements GrammarSlot { + + // Record failures, it's cheaper for some complex regular expressions to do a lookup than to match again + private static final Object failure = "failure"; + + private final Terminal terminal; + private final Matcher matcher; + private IntHashMap terminalNodes; + + public TerminalGrammarSlot(Terminal terminal, MatcherFactory factory) { + this.terminal = terminal; + this.matcher = factory.getMatcher(terminal.getRegularExpression()); + } + + public T getResult(Input input, int i, IguanaRuntime runtime) { + if (terminalNodes == null) { + terminalNodes = new OpenAddressingIntHashMap<>(); + } + Object node = terminalNodes.get(i); + if (node == failure) return null; + + @SuppressWarnings("unchecked") + T terminalNode = (T) node; + if (terminalNode == null) { + int length = matcher.match(input, i); + if (length < 0) { + terminalNodes.put(i, failure); + } else { + terminalNode = runtime.getResultOps().base(this, i, i + length); + terminalNodes.put(i, terminalNode); + } + } + return terminalNode; + } + + + public void recordFailure(int index) { + if (terminalNodes == null) { + terminalNodes = new OpenAddressingIntHashMap<>(); + } + terminalNodes.put(index, failure); + } + + public int countTerminalNodes() { + return terminalNodes.size(); + } + + public Terminal getTerminal() { + return terminal; + } + + @Override + public String toString() { + return terminal.toString(); + } + + @Override + public void reset() { + terminalNodes = null; + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/grammar/slot/TerminalNodeType.java b/benchmarks/src/main/kotlin/org/iguana/grammar/slot/TerminalNodeType.java new file mode 100644 index 000000000..a1e77922a --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/grammar/slot/TerminalNodeType.java @@ -0,0 +1,7 @@ +package org.iguana.grammar.slot; + +public enum TerminalNodeType { + Regex, + Layout, + Literal +} diff --git a/benchmarks/src/main/kotlin/org/iguana/grammar/slot/TerminalTransition.java b/benchmarks/src/main/kotlin/org/iguana/grammar/slot/TerminalTransition.java new file mode 100644 index 000000000..afe4fe543 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/grammar/slot/TerminalTransition.java @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.grammar.slot; + +import org.iguana.datadependent.ast.Expression; +import org.iguana.datadependent.env.Environment; +import org.iguana.grammar.condition.Conditions; +import org.iguana.gss.GSSNode; +import org.iguana.parser.IguanaRuntime; +import org.iguana.result.Result; +import org.iguana.utils.input.Input; + +public class TerminalTransition extends AbstractTransition { + + protected final TerminalGrammarSlot terminalSlot; + + private final Conditions preConditions; + + private final Conditions postConditions; + + public TerminalTransition( + TerminalGrammarSlot terminalSlot, + BodyGrammarSlot origin, + BodyGrammarSlot dest, + Conditions preConditions, + Conditions postConditions) { + super(origin, dest); + this.terminalSlot = terminalSlot; + this.preConditions = preConditions; + this.postConditions = postConditions; + } + + public TerminalGrammarSlot getTerminalSlot() { + return terminalSlot; + } + + @Override + public String getLabel() { + return (dest.getLabel() != null ? dest.getLabel() + ":" : "") + getTerminalSlot(); + } + + @Override + public void execute( + Input input, + GSSNode u, + T result, + Environment env, + IguanaRuntime runtime) { + int i = result.isDummy() ? u.getInputIndex() : result.getRightExtent(); + + runtime.setEnvironment(env); + + if (dest.getLabel() != null) + runtime.getEvaluatorContext().declareVariable( + String.format(Expression.LeftExtent.format, dest.getLabel()), i); + + if (preConditions.execute(input, origin, u, i, runtime.getEvaluatorContext(), runtime)) { + terminalSlot.recordFailure(i); + return; + } + + T cr = terminalSlot.getResult(input, i, runtime); + + if (cr == null) { + runtime.recordParseError(i, input, origin, u, "Match failed"); + return; + } + + if (dest.getLabel() != null) { + runtime.getEvaluatorContext().declareVariable(dest.getLabel(), cr); + } + + if (postConditions.execute(input, origin, u, cr.getLeftExtent(), cr.getRightExtent(), + runtime.getEvaluatorContext(), runtime)) { + terminalSlot.recordFailure(cr.getRightExtent()); + return; + } + + T n = dest.isFirst() ? cr : runtime.getResultOps().merge(null, result, cr, dest); + + dest.execute(input, u, n, runtime.getEnvironment(), runtime); + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/grammar/slot/Transition.java b/benchmarks/src/main/kotlin/org/iguana/grammar/slot/Transition.java new file mode 100644 index 000000000..06e476dd4 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/grammar/slot/Transition.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.grammar.slot; + +import org.iguana.datadependent.env.Environment; +import org.iguana.gss.GSSNode; +import org.iguana.parser.IguanaRuntime; +import org.iguana.result.Result; +import org.iguana.utils.input.Input; + + +public interface Transition { + + BodyGrammarSlot destination(); + + BodyGrammarSlot origin(); + + String getLabel(); + + void execute(Input input, GSSNode u, T result, Environment env, IguanaRuntime runtime); + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/grammar/slot/lookahead/FollowTest.java b/benchmarks/src/main/kotlin/org/iguana/grammar/slot/lookahead/FollowTest.java new file mode 100644 index 000000000..23d7d4fd0 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/grammar/slot/lookahead/FollowTest.java @@ -0,0 +1,8 @@ +package org.iguana.grammar.slot.lookahead; + +@FunctionalInterface +public interface FollowTest { + FollowTest DEFAULT = i -> true; + + boolean test(int v); +} diff --git a/benchmarks/src/main/kotlin/org/iguana/grammar/slot/lookahead/RangeTreeFollowTest.java b/benchmarks/src/main/kotlin/org/iguana/grammar/slot/lookahead/RangeTreeFollowTest.java new file mode 100644 index 000000000..f597f227f --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/grammar/slot/lookahead/RangeTreeFollowTest.java @@ -0,0 +1,30 @@ +package org.iguana.grammar.slot.lookahead; + +import org.iguana.regex.CharRange; +import org.iguana.utils.collections.rangemap.IntRangeMap; +import org.iguana.utils.collections.rangemap.RangeMapBuilder; + +import java.util.Set; + +public class RangeTreeFollowTest implements FollowTest { + + private final IntRangeMap rangeMap; + private final String followTestToString; + + public RangeTreeFollowTest(Set set) { + RangeMapBuilder builder = new RangeMapBuilder<>(); + set.forEach(r -> builder.put(r, 1)); + rangeMap = builder.buildIntRangeMap(); + followTestToString = set.toString(); + } + + @Override + public boolean test(int v) { + return rangeMap.get(v) == 1; + } + + @Override + public String toString() { + return followTestToString; + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/grammar/symbol/AbstractSymbol.java b/benchmarks/src/main/kotlin/org/iguana/grammar/symbol/AbstractSymbol.java new file mode 100644 index 000000000..b74ddb025 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/grammar/symbol/AbstractSymbol.java @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.grammar.symbol; + +import org.iguana.datadependent.attrs.AbstractAttrs; +import org.iguana.grammar.condition.Condition; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import static org.iguana.utils.string.StringUtil.listToString; + +public abstract class AbstractSymbol extends AbstractAttrs implements Symbol { + + protected final String name; + + protected final String label; + + protected final List preConditions; + + protected final List postConditions; + + private final Map attributes; + + public AbstractSymbol(SymbolBuilder builder) { + if (builder.name == null) + throw new IllegalArgumentException("Name cannot be null"); + this.name = builder.name; + this.label = builder.label; + this.preConditions = builder.preConditions.isEmpty() ? Collections.emptyList() : builder.preConditions; + this.postConditions = builder.postConditions.isEmpty() ? Collections.emptyList() : builder.postConditions; + this.attributes = builder.attributes.isEmpty() ? Collections.emptyMap() : builder.attributes; + } + + @Override + public List getPreConditions() { + return preConditions; + } + + @Override + public List getPostConditions() { + return postConditions; + } + + @Override + public String getName() { + return name; + } + + @Override + public String getLabel() { + return label; + } + + @Override + public String toString() { + String s = label == null ? name : label + ":" + name; + if (!preConditions.isEmpty()) + s += " " + listToString(preConditions); + if (!postConditions.isEmpty()) + s += " " + listToString(postConditions); + return s; + } + + @Override + public String toString(int j) { + return this + (j == 1 ? " . " : ""); + } + + @Override + public Map getAttributes() { + return attributes; + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/grammar/symbol/Align.java b/benchmarks/src/main/kotlin/org/iguana/grammar/symbol/Align.java new file mode 100644 index 000000000..908c7837e --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/grammar/symbol/Align.java @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.grammar.symbol; + +import org.iguana.traversal.ISymbolVisitor; + +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +public class Align extends AbstractSymbol { + + private final Symbol symbol; + + Align(Builder builder) { + super(builder); + this.symbol = builder.symbol; + } + + public static Align align(Symbol symbol) { + return new Builder(symbol).build(); + } + + public Symbol getSymbol() { + return symbol; + } + + @Override + public Builder copy() { + return new Builder(this); + } + + @Override + public int size() { + return symbol.size(); + } + + @Override + public String toString() { + return super.toString(); + } + + @Override + public String toString(int j) { + return String.format("align %s", symbol.toString(j)); + } + + @Override + public List getChildren() { + return Collections.singletonList(symbol); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Align)) return false; + Align align = (Align) o; + return Objects.equals(symbol, align.symbol); + } + + @Override + public int hashCode() { + return Objects.hash(symbol); + } + + public static class Builder extends SymbolBuilder { + + private Symbol symbol; + + public Builder() { } + + public Builder(Align align) { + super(align); + this.symbol = align.symbol; + } + + public Builder(Symbol symbol) { + this.symbol = symbol; + } + + @Override + public SymbolBuilder setChildren(List symbols) { + this.symbol = symbols.get(0); + return this; + } + + @Override + public Align build() { + this.name = String.format("align %s", symbol.toString()); + return new Align(this); + } + + } + + @Override + public T accept(ISymbolVisitor visitor) { + return visitor.visit(this); + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/grammar/symbol/Alt.java b/benchmarks/src/main/kotlin/org/iguana/grammar/symbol/Alt.java new file mode 100644 index 000000000..6b21bf7e9 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/grammar/symbol/Alt.java @@ -0,0 +1,141 @@ + +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.grammar.symbol; + +import org.iguana.traversal.ISymbolVisitor; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +public class Alt extends AbstractSymbol { + + protected final List symbols; + + public Alt(Builder builder) { + super(builder); + this.symbols = builder.symbols; + } + + public static Alt from(Symbol... symbols) { + return from(Arrays.asList(symbols)); + } + + public static Alt from(List list) { + return new Builder(list).build(); + } + + public int size() { + return symbols.size(); + } + + public Symbol get(int index) { + return symbols.get(index); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) + return true; + + if (!(obj instanceof Alt)) + return false; + + Alt other = (Alt) obj; + + return other.symbols.equals(symbols); + } + + @Override + public int hashCode() { + return symbols.hashCode(); + } + + @Override + public Builder copy() { + return new Builder(this); + } + + public List getSymbols() { + return symbols; + } + + @Override + public List getChildren() { + return symbols; + } + + public static class Builder extends SymbolBuilder { + + private List symbols; + + private Builder() {} + + public Builder(Symbol... symbols) { + this(Arrays.asList(symbols)); + } + + public Builder(List symbols) { + this.symbols = symbols; + } + + public Builder(Alt alt) { + super(alt); + this.symbols = alt.getSymbols(); + } + + public Builder add(Symbol symbol) { + symbols.add(symbol); + return this; + } + + public Builder add(List l) { + symbols.addAll(l); + return this; + } + + @Override + public SymbolBuilder setChildren(List symbols) { + this.symbols = symbols; + return this; + } + + @Override + public Alt build() { + this.name = "(" + symbols.stream().map(Symbol::getName).collect(Collectors.joining(" | ")) + ")"; + return new Alt(this); + } + } + + @Override + public E accept(ISymbolVisitor visitor) { + return visitor.visit(this); + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/grammar/symbol/Alternative.java b/benchmarks/src/main/kotlin/org/iguana/grammar/symbol/Alternative.java new file mode 100644 index 000000000..a1ba79106 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/grammar/symbol/Alternative.java @@ -0,0 +1,143 @@ +package org.iguana.grammar.symbol; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +import static org.iguana.utils.collections.CollectionsUtil.buildList; + +public class Alternative { + + private final List seqs; + private final Associativity associativity; + + public Alternative(Builder builder) { + this.seqs = builder.seqs; + this.associativity = builder.associativity; + } + + public static Alternative from(Sequence... seqs) { + return new Builder().addSequences(List.of(seqs)).build(); + } + + public Sequence first() { + if (seqs.isEmpty()) return null; + return seqs.get(0); + } + + public List rest() { + if (seqs.size() < 2) return null; + return seqs.subList(1, seqs.size()); + } + + public Builder copy() { + return new Builder(this); + } + + public Associativity getAssociativity() { + return associativity; + } + + public List seqs() { + return seqs; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (!(obj instanceof Alternative)) return false; + Alternative other = (Alternative) obj; + return Objects.equals(this.seqs(), other.seqs()) && Objects.equals(this.associativity, other.associativity); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + if (seqs.size() > 1) { + if (associativity != Associativity.UNDEFINED) { + sb.append(associativity).append(" "); + } + sb.append("("); + for (Sequence seq : seqs) { + sb.append(seq).append("\n | "); + } + sb.delete(sb.length() - 11, sb.length()); + sb.append(")"); + + } else if (seqs.size() == 1) { + if (associativity != Associativity.UNDEFINED) { + sb.append(associativity).append(" ").append(seqs.get(0)); + } else { + sb.append(seqs.get(0)); + } + } + return sb.toString(); + } + + public static class Builder { + private List seqs = new ArrayList<>(); + private Associativity associativity = Associativity.UNDEFINED; + + public Builder() { + } + + public Builder(Alternative alternative) { + this.seqs = new ArrayList<>(alternative.seqs); + this.associativity = alternative.associativity; + } + + public Builder(List sequences, Associativity associativity) { + if (sequences == null) throw new RuntimeException("Sequences cannot be null."); + if (associativity == null) throw new RuntimeException("Sequences cannot be null."); + this.seqs = sequences; + this.associativity = associativity; + } + + public Builder addSequence(Sequence seq) { + seqs.add(seq); + return this; + } + + public Builder addSequences(List seqs) { + this.seqs.addAll(seqs); + return this; + } + + public Builder clearSequences() { + this.seqs.clear(); + return this; + } + + public Builder setAssociativity(Associativity associativity) { + this.associativity = associativity; + return this; + } + + public Alternative build() { + if (associativity == null) { + throw new RuntimeException("Associativity cannot be null."); + } + // Based on the grammar, associativity cannot be undefined for associativity groups. + // left (E '+' E | E '-' E) will translate into left (left E '+' E | left E '-' E). + // In other words, if the individual sequences do not define the associativity, they will + // inherit the associativity of the group. + if (associativity != Associativity.UNDEFINED) { + if (seqs.size() < 2) + throw new RuntimeException("Associativity groups should have at least two sequences: " + seqs); + + List seqsWithAssociativity = new ArrayList<>(seqs.size()); + for (Sequence seq : seqs) { + if (seq.getAssociativity() == Associativity.UNDEFINED) { + seqsWithAssociativity.add(seq.copy().setAssociativity(associativity).build()); + } else { + seqsWithAssociativity.add(seq); + } + } + seqs = seqsWithAssociativity; + } + + seqs = buildList(seqs); + return new Alternative(this); + } + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/grammar/symbol/Associativity.java b/benchmarks/src/main/kotlin/org/iguana/grammar/symbol/Associativity.java new file mode 100644 index 000000000..8cb13ac7f --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/grammar/symbol/Associativity.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.grammar.symbol; + +public enum Associativity { + LEFT("left"), + RIGHT("right"), + NON_ASSOC("non_assoc"), + UNDEFINED(""); + + private final String name; + + Associativity(String name) { + this.name = name; + } + + @Override + public String toString() { + return name; + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/grammar/symbol/Block.java b/benchmarks/src/main/kotlin/org/iguana/grammar/symbol/Block.java new file mode 100644 index 000000000..49870b0d8 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/grammar/symbol/Block.java @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.grammar.symbol; + +import org.iguana.traversal.ISymbolVisitor; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import static org.iguana.utils.string.StringUtil.listToString; + +public class Block extends AbstractSymbol { + + private final List symbols; + + Block(Builder builder) { + super(builder); + this.symbols = builder.symbols; + } + + public static Block block(Symbol... symbols) { + return new Builder(symbols).build(); + } + + public List getSymbols() { + return symbols; + } + + @Override + public Builder copy() { + return new Builder(this); + } + + @Override + public List getChildren() { + return symbols; + } + + @Override + public int size() { + int size = 0; + for (Symbol symbol : symbols) + size = size + symbol.size(); + return size; + } + + @Override + public String toString() { + return String.format("{ %s }", listToString(symbols, " ")); + } + + @Override + public String toString(int j) { + List strings = new ArrayList<>(symbols.size()); + + int k = 0; + for (Symbol symbol : symbols) { + strings.set(k, j <= 1 ? symbol.toString(j) : symbol.toString()); + j = j - symbol.size(); + k++; + } + + return String.format("{ %s }", listToString(strings, " ")); + } + + public static class Builder extends SymbolBuilder { + + private List symbols; + + public Builder(Block block) { + super(block); + this.symbols = block.symbols; + } + + public Builder(Symbol... symbols) { + assert symbols.length != 0; + + this.symbols = Arrays.asList(symbols); + } + + @Override + public SymbolBuilder setChildren(List symbols) { + this.symbols = new ArrayList<>(symbols); + return this; + } + + @Override + public Block build() { + this.name = String.format("{ %s }", listToString(symbols, " ")); + return new Block(this); + } + + } + + @Override + public T accept(ISymbolVisitor visitor) { + return visitor.visit(this); + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/grammar/symbol/Code.java b/benchmarks/src/main/kotlin/org/iguana/grammar/symbol/Code.java new file mode 100644 index 000000000..e7a703b74 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/grammar/symbol/Code.java @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.grammar.symbol; + +import org.iguana.datadependent.ast.Statement; +import org.iguana.traversal.ISymbolVisitor; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +import static org.iguana.utils.string.StringUtil.listToString; + +public class Code extends AbstractSymbol { + + private final Symbol symbol; + private final Statement[] statements; + + Code(Builder builder) { + super(builder); + this.symbol = builder.symbol; + this.statements = builder.statements; + } + + public static Code code(Symbol symbol, Statement... statements) { + return new Builder(symbol, statements).build(); + } + + public Symbol getSymbol() { + return symbol; + } + + public Statement[] getStatements() { + return statements; + } + + @Override + public Builder copy() { + return new Builder(this); + } + + @Override + public List getChildren() { + return Collections.singletonList(symbol); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Code)) return false; + Code code = (Code) o; + return Objects.equals(symbol, code.symbol) && Arrays.equals(statements, code.statements); + } + + @Override + public int hashCode() { + int result = Objects.hash(symbol); + result = 31 * result + Arrays.hashCode(statements); + return result; + } + + @Override + public String toString() { + return String.format("%s {%s}", symbol.toString(), listToString(statements, ";")); + } + + public static class Builder extends SymbolBuilder { + + private Symbol symbol; + private Statement[] statements; + + public Builder() { } + + public Builder(Code code) { + super(code); + this.symbol = code.symbol; + this.statements = code.statements; + } + + public Builder(Symbol symbol, Statement... statements) { + assert statements.length != 0; + + this.symbol = symbol; + this.statements = statements; + } + + @Override + public SymbolBuilder setChildren(List symbols) { + this.symbol = symbols.get(0); + return this; + } + + @Override + public Code build() { + this.name = String.format("%s {%s}", symbol.toString(), listToString(statements, ";")); + return new Code(this); + } + + } + + @Override + public T accept(ISymbolVisitor visitor) { + return visitor.visit(this); + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/grammar/symbol/CodeHolder.java b/benchmarks/src/main/kotlin/org/iguana/grammar/symbol/CodeHolder.java new file mode 100644 index 000000000..93f45b884 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/grammar/symbol/CodeHolder.java @@ -0,0 +1,109 @@ +package org.iguana.grammar.symbol; + +import org.iguana.datadependent.ast.Statement; +import org.iguana.grammar.condition.Condition; +import org.iguana.traversal.ISymbolVisitor; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +public class CodeHolder implements org.iguana.grammar.symbol.Symbol { + + public final Statement statement; + + public CodeHolder(Builder builder) { + this.statement = builder.statement; + } + + public CodeHolder(Statement statement) { + this.statement = statement; + } + + @Override + public String getName() { + return null; + } + + @Override + public List getPreConditions() { + return Collections.emptyList(); + } + + @Override + public List getPostConditions() { + return Collections.emptyList(); + } + + @Override + public String getLabel() { + return null; + } + + @Override + public Builder copy() { + return new Builder(this); + } + + @Override + public String toString(int j) { + return null; + } + + @Override + public Map getAttributes() { + return Collections.emptyMap(); + } + + @Override + public T accept(ISymbolVisitor visitor) { + return visitor.visit(this); + } + + @Override + public io.usethesource.capsule.Set.Immutable getEnv() { + return null; + } + + @Override + public void setEnv(io.usethesource.capsule.Set.Immutable env) { + } + + @Override + public void setEmpty() { + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof CodeHolder)) return false; + CodeHolder that = (CodeHolder) o; + return Objects.equals(statement, that.statement); + } + + @Override + public int hashCode() { + return Objects.hash(statement); + } + + public static class Builder extends SymbolBuilder { + + public Statement statement; + + public Builder() {} + + public Builder(CodeHolder codeHolder) { + this.statement = codeHolder.statement; + } + + public void setStatement(Statement statement) { + this.statement = statement; + } + + @Override + public CodeHolder build() { + return new CodeHolder(this); + } + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/grammar/symbol/Conditional.java b/benchmarks/src/main/kotlin/org/iguana/grammar/symbol/Conditional.java new file mode 100644 index 000000000..4ec5e5417 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/grammar/symbol/Conditional.java @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.grammar.symbol; + +import org.iguana.datadependent.ast.Expression; +import org.iguana.traversal.ISymbolVisitor; + +import java.util.Collections; +import java.util.List; + +public class Conditional extends AbstractSymbol { + + private final Symbol symbol; + private final Expression expression; + + Conditional(Builder builder) { + super(builder); + this.symbol = builder.symbol; + this.expression = builder.expression; + } + + public static Conditional when(Symbol symbol, Expression expression) { + return new Builder(symbol, expression).build(); + } + + public Symbol getSymbol() { + return symbol; + } + + public Expression getExpression() { + return expression; + } + + @Override + public Builder copy() { + return new Builder(this); + } + + @Override + public List getChildren() { + return Collections.singletonList(symbol); + } + + @Override + public int size() { + return symbol.size(); + } + + @Override + public String toString() { + return String.format("%s when %s", symbol.toString(), expression.toString()); + } + + @Override + public String toString(int j) { + return String.format(" %s when %s", symbol.toString(j), expression.toString()); + } + + public static class Builder extends SymbolBuilder { + + private Symbol symbol; + private Expression expression; + + public Builder(Conditional conditional) { + super(conditional); + this.symbol = conditional.symbol; + this.expression = conditional.expression; + } + + public Builder(Symbol symbol, Expression expression) { + this.symbol = symbol; + this.expression = expression; + } + + @Override + public SymbolBuilder setChildren(List symbols) { + this.symbol = symbols.get(0); + return this; + } + + @Override + public Conditional build() { + this.name = String.format("%s when %s;", symbol.toString(), expression.toString()); + return new Conditional(this); + } + } + + @Override + public T accept(ISymbolVisitor visitor) { + return visitor.visit(this); + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/grammar/symbol/Error.java b/benchmarks/src/main/kotlin/org/iguana/grammar/symbol/Error.java new file mode 100644 index 000000000..44230032a --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/grammar/symbol/Error.java @@ -0,0 +1,75 @@ +package org.iguana.grammar.symbol; + +import org.iguana.datadependent.attrs.AbstractAttrs; +import org.iguana.grammar.condition.Condition; +import org.iguana.traversal.ISymbolVisitor; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +public class Error extends AbstractAttrs implements Symbol { + + private static final Error instance = new Error(); + + private Error() { } + + public static Error getInstance() { + return instance; + } + + @Override + public String getName() { + return "Error"; + } + + @Override + public List getPreConditions() { + return Collections.emptyList(); + } + + @Override + public List getPostConditions() { + return Collections.emptyList(); + } + + @Override + public String getLabel() { + return null; + } + + @Override + public SymbolBuilder copy() { + throw new UnsupportedOperationException(); + } + + @Override + public String toString(int j) { + throw new UnsupportedOperationException(); + } + + @Override + public Map getAttributes() { + throw new UnsupportedOperationException(); + } + + @Override + public T accept(ISymbolVisitor visitor) { + return visitor.visit(this); + } + + @Override + public String toString() { + return "Error"; + } + + @Override + public boolean equals(Object obj) { + return obj == instance; + } + + @Override + public int hashCode() { + return System.identityHashCode(instance); + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/grammar/symbol/Group.java b/benchmarks/src/main/kotlin/org/iguana/grammar/symbol/Group.java new file mode 100644 index 000000000..a783a2a42 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/grammar/symbol/Group.java @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.grammar.symbol; + +import org.iguana.traversal.ISymbolVisitor; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +import static org.iguana.utils.string.StringUtil.listToString; + +/** + * A group represents a grouping of symbols, e.g, (A B C) in the EBNF notation. + */ +public class Group extends AbstractSymbol { + + private final List symbols; + + private Group(Builder builder) { + super(builder); + this.symbols = builder.symbols; + } + + public static Group from(List symbols) { + return new Builder(symbols).build(); + } + + public static Group from(Symbol... symbols) { + return from(Arrays.asList(symbols)); + } + + public int size() { + return symbols.size(); + } + + public Symbol get(int index) { + return symbols.get(index); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) + return true; + + if (!(obj instanceof Group)) + return false; + + Group other = (Group) obj; + + return symbols.equals(other.symbols); + } + + @Override + public int hashCode() { + return name.hashCode(); + } + + @Override + public Builder copy() { + return new Builder(this); + } + + @Override + public List getChildren() { + return symbols; + } + + public List getSymbols() { + return symbols; + } + + @Override + public String toString() { + + String body = "(" + listToString(symbols, " ") + ")"; + + String s = label == null ? body : label + ":" + body; + if (!preConditions.isEmpty()) + s += " " + listToString(preConditions); + if (!postConditions.isEmpty()) + s += " " + listToString(postConditions); + return s; + } + + public static class Builder extends SymbolBuilder { + + private List symbols = new ArrayList<>(); + + private Builder() { } + + public Builder(Symbol... symbols) { + this(Arrays.asList(symbols)); + } + + public Builder(List symbols) { + this.symbols = symbols; + } + + public Builder(Group seq) { + super(seq); + this.symbols = seq.symbols; + } + + public Builder add(Symbol s) { + symbols.add(s); + return this; + } + + public Builder add(List symbols) { + this.symbols.addAll(symbols); + return this; + } + + @Override + public SymbolBuilder setChildren(List symbols) { + this.symbols = symbols; + return this; + } + + @Override + public Group build() { + this.name = "(" + symbols.stream().map(Symbol::getName).collect(Collectors.joining(" ")) + ")"; + return new Group(this); + } + } + + @Override + public E accept(ISymbolVisitor visitor) { + return visitor.visit(this); + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/grammar/symbol/Identifier.java b/benchmarks/src/main/kotlin/org/iguana/grammar/symbol/Identifier.java new file mode 100644 index 000000000..a718b9c96 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/grammar/symbol/Identifier.java @@ -0,0 +1,73 @@ +package org.iguana.grammar.symbol; + +import org.iguana.traversal.ISymbolVisitor; + +import java.util.HashSet; +import java.util.Set; + +public class Identifier extends AbstractSymbol { + + private final Set excepts; + + public Identifier(Builder builder) { + super(builder); + this.excepts = builder.excepts; + } + + public static Identifier fromName(String name) { + return new Builder(name).build(); + } + + public Set getExcepts() { + return excepts; + } + + @Override + public Builder copy() { + return new Builder(this); + } + + @Override + public T accept(ISymbolVisitor visitor) { + return visitor.visit(this); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (!(obj instanceof Identifier)) return false; + Identifier identifier = (Identifier) obj; + return identifier.name.equals(name); + } + + @Override + public int hashCode() { + return name.hashCode(); + } + + public static class Builder extends SymbolBuilder { + + private Set excepts = new HashSet<>(); + + private Builder() { } + + public Builder(String name) { + this.name = name; + } + + public Builder(Identifier identifier) { + super(identifier); + this.excepts = identifier.excepts; + } + + public Builder addExcept(String label) { + excepts.add(label); + return this; + } + + @Override + public Identifier build() { + return new Identifier(this); + } + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/grammar/symbol/IfThen.java b/benchmarks/src/main/kotlin/org/iguana/grammar/symbol/IfThen.java new file mode 100644 index 000000000..8c8773488 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/grammar/symbol/IfThen.java @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.grammar.symbol; + +import org.iguana.datadependent.ast.Expression; +import org.iguana.regex.Epsilon; +import org.iguana.traversal.ISymbolVisitor; + +import java.util.Collections; +import java.util.List; + +public class IfThen extends AbstractSymbol { + + private final Expression expression; + private final Symbol thenPart; + + IfThen(Builder builder) { + super(builder); + this.expression = builder.expression; + this.thenPart = builder.thenPart; + } + + public static IfThen ifThen(Expression expression, Symbol thenPart) { + return new Builder(expression, thenPart).build(); + } + + public Expression getExpression() { + return expression; + } + + public Symbol getThenPart() { + return thenPart; + } + + @Override + public Builder copy() { + return new Builder(this); + } + + @Override + public List getChildren() { + return Collections.singletonList(thenPart); + } + + @Override + public int size() { + return thenPart.size() + 1; + } + + @Override + public String toString() { + return String.format("if (%s) %s", expression.toString(), thenPart.toString()); + } + + @Override + public String toString(int j) { + return String.format("if (%s) { %s } else %s", + expression.toString(), + thenPart.toString(j), + j - thenPart.size() <= 1 ? epsilonToString(j - thenPart.size()) + : Epsilon.getInstance().toString()); + } + + public String epsilonToString(int j) { + return Epsilon.getInstance().toString() + (j == 1 ? " . " : ""); + } + + public static class Builder extends SymbolBuilder { + + private Expression expression; + private Symbol thenPart; + + public Builder(IfThen ifThen) { + super(ifThen); + this.expression = ifThen.expression; + this.thenPart = ifThen.thenPart; + } + + public Builder(Expression expression, Symbol thenPart) { + this.expression = expression; + this.thenPart = thenPart; + } + + @Override + public SymbolBuilder setChildren(List symbols) { + this.thenPart = symbols.get(0); + return this; + } + + @Override + public IfThen build() { + this.name = String.format("if (%s) %s", expression.toString(), thenPart.toString()); + return new IfThen(this); + } + + } + + @Override + public T accept(ISymbolVisitor visitor) { + return visitor.visit(this); + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/grammar/symbol/IfThenElse.java b/benchmarks/src/main/kotlin/org/iguana/grammar/symbol/IfThenElse.java new file mode 100644 index 000000000..ce0b15421 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/grammar/symbol/IfThenElse.java @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.grammar.symbol; + +import org.iguana.datadependent.ast.Expression; +import org.iguana.traversal.ISymbolVisitor; + +import java.util.Arrays; +import java.util.List; +import java.util.Objects; + +public class IfThenElse extends AbstractSymbol { + + private final Expression expression; + private final Symbol thenPart; + private final Symbol elsePart; + + IfThenElse(Builder builder) { + super(builder); + this.expression = builder.expression; + this.thenPart = builder.thenPart; + this.elsePart = builder.elsePart; + } + + public static IfThenElse ifThenElse(Expression expression, Symbol thenPart, Symbol elsePart) { + return new Builder(expression, thenPart, elsePart).build(); + } + + public Expression getExpression() { + return expression; + } + + public Symbol getThenPart() { + return thenPart; + } + + public Symbol getElsePart() { + return elsePart; + } + + @Override + public Builder copy() { + return new Builder(this); + } + + @Override + public List getChildren() { + return Arrays.asList(thenPart, elsePart); + } + + @Override + public String toString() { + return String.format("if (%s) %s else %s", expression.toString(), thenPart.toString(), elsePart.toString()); + } + + @Override + public int size() { + return thenPart.size() + elsePart.size(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof IfThenElse)) return false; + IfThenElse that = (IfThenElse) o; + return Objects.equals(expression, that.expression) + && Objects.equals(thenPart, that.thenPart) + && Objects.equals(elsePart, that.elsePart); + } + + @Override + public int hashCode() { + return Objects.hash(expression, thenPart, elsePart); + } + + @Override + public String toString(int j) { + return String.format("if (%s) { %s } else { %s }", + expression.toString(), + thenPart.toString(j), + j - thenPart.size() <= 1 + ? elsePart.toString(j - thenPart.size()) + : elsePart.toString()); + } + + public static class Builder extends SymbolBuilder { + + private Expression expression; + private Symbol thenPart; + private Symbol elsePart; + + public Builder() { } + + public Builder(IfThenElse ifThenElse) { + super(ifThenElse); + this.expression = ifThenElse.expression; + this.thenPart = ifThenElse.thenPart; + this.elsePart = ifThenElse.elsePart; + } + + public Builder(Expression expression, Symbol thenPart, Symbol elsePart) { + this.expression = expression; + this.thenPart = thenPart; + this.elsePart = elsePart; + } + + @Override + public SymbolBuilder setChildren(List symbols) { + this.thenPart = symbols.get(0); + this.elsePart = symbols.get(1); + return this; + } + + @Override + public IfThenElse build() { + this.name = String.format("if (%s) %s else %s;", expression, thenPart, elsePart); + return new IfThenElse(this); + } + + } + + @Override + public T accept(ISymbolVisitor visitor) { + return visitor.visit(this); + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/grammar/symbol/Ignore.java b/benchmarks/src/main/kotlin/org/iguana/grammar/symbol/Ignore.java new file mode 100644 index 000000000..e6579066c --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/grammar/symbol/Ignore.java @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.grammar.symbol; + +import org.iguana.traversal.ISymbolVisitor; + +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +public class Ignore extends AbstractSymbol { + + private final Symbol symbol; + + Ignore(Builder builder) { + super(builder); + this.symbol = builder.symbol; + } + + public static Ignore ignore(Symbol symbol) { + return new Builder(symbol).build(); + } + + public Symbol getSymbol() { + return symbol; + } + + @Override + public Builder copy() { + return new Builder(this); + } + + @Override + public List getChildren() { + return Collections.singletonList(symbol); + } + + @Override + public int size() { + return symbol.size(); + } + + @Override + public String toString() { + return super.toString(); + } + + @Override + public String toString(int j) { + return String.format("ignore %s", symbol.toString(j)); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Ignore)) return false; + Ignore ignore = (Ignore) o; + return Objects.equals(symbol, ignore.symbol); + } + + @Override + public int hashCode() { + return Objects.hash(symbol); + } + + public static class Builder extends SymbolBuilder { + + private Symbol symbol; + + public Builder() { } + + public Builder(Ignore ignore) { + super(ignore); + this.symbol = ignore.symbol; + } + + public Builder(Symbol symbol) { + this.symbol = symbol; + } + + @Override + public SymbolBuilder setChildren(List symbols) { + this.symbol = symbols.get(0); + return this; + } + + @Override + public Ignore build() { + this.name = String.format("ignore %s", symbol.toString()); + return new Ignore(this); + } + } + + @Override + public T accept(ISymbolVisitor visitor) { + return visitor.visit(this); + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/grammar/symbol/LayoutStrategy.java b/benchmarks/src/main/kotlin/org/iguana/grammar/symbol/LayoutStrategy.java new file mode 100644 index 000000000..70dd6b818 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/grammar/symbol/LayoutStrategy.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.grammar.symbol; + +public enum LayoutStrategy { + NO_LAYOUT, + INHERITED, + FIXED +} diff --git a/benchmarks/src/main/kotlin/org/iguana/grammar/symbol/Nonterminal.java b/benchmarks/src/main/kotlin/org/iguana/grammar/symbol/Nonterminal.java new file mode 100644 index 000000000..75d6264ae --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/grammar/symbol/Nonterminal.java @@ -0,0 +1,344 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.grammar.symbol; + +import org.iguana.datadependent.ast.Expression; +import org.iguana.grammar.condition.Condition; +import org.iguana.grammar.slot.NonterminalNodeType; +import org.iguana.traversal.ISymbolVisitor; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static org.iguana.utils.string.StringUtil.listToString; + +public class Nonterminal extends AbstractSymbol { + + private final boolean ebnfList; + + private final int index; + + private final String variable; + + private final Set state; + + private final List parameters; // Only head + + private final Expression[] arguments; + + private final Set excepts; + + /** + * The type of this nonterminal. This field is used to track EBNF to BNF conversion + * information for each nonterminal. See NonterminalNodeType. + */ + private final NonterminalNodeType nodeType; + + protected Nonterminal(Builder builder) { + super(builder); + this.ebnfList = builder.ebnfList; + this.index = builder.index; + this.variable = builder.variable; + this.state = builder.state; + this.parameters = builder.parameters; + this.arguments = builder.arguments; + this.excepts = builder.excepts; + this.nodeType = builder.nodeType; + } + + public static Nonterminal withName(String name) { + return new Builder(name).build(); + } + + public boolean isEbnfList() { + if (ebnfList) { + return true; + } else { + return name.startsWith("List"); + } + } + + public int getIndex() { + return index; + } + + public String getVariable() { + return variable; + } + + public Set getState() { + return state; + } + + public List getParameters() { + return parameters; + } + + public Expression[] getArguments() { + return arguments; + } + + public Set getExcepts() { + return excepts; + } + + public NonterminalNodeType getNodeType() { + return nodeType; + } + + @Override + public String toString() { + return (variable != null ? variable + (state == null || state.isEmpty() ? "=" : ":") : "") + + (state != null && !state.isEmpty() ? listToString(state, ":") + "=" : "") + + (getPreConditions().isEmpty() ? "" : listToString(getPreConditions(), ",")) + + (label != null ? label + ":" : "") + + name + (index > 0 ? index : "") + + (arguments == null && parameters != null ? "(" + listToString(parameters, ",") + ")" : "") + + (arguments != null ? "(" + listToString(arguments, ",") + ")" : "") + + (getPostConditions().isEmpty() ? "" : listToString(getPostConditions(), ",")); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + + if (!(obj instanceof Nonterminal)) + return false; + + Nonterminal other = (Nonterminal) obj; + + return getEffectiveName().equals(other.getEffectiveName()); + } + + private String getEffectiveName() { + return name + (index > 0 ? index : ""); + } + + @Override + public int hashCode() { + return getEffectiveName().hashCode(); + } + + @Override + public Builder copy() { + return new Builder(this); + } + + public static class Builder extends SymbolBuilder { + + private boolean ebnfList; + + private int index; + + private String variable; + + private Set state; + + private List parameters; // Only head + + private Expression[] arguments; + + private Set excepts; + + private NonterminalNodeType nodeType = NonterminalNodeType.Basic; + + private Map attributes = new HashMap<>(); + + public Builder(Nonterminal nonterminal) { + super(nonterminal); + this.ebnfList = nonterminal.ebnfList; + this.index = nonterminal.index; + this.variable = nonterminal.variable; + this.state = nonterminal.state; + this.parameters = nonterminal.parameters; + this.arguments = nonterminal.arguments; + this.excepts = nonterminal.excepts; + this.nodeType = nonterminal.nodeType; + } + + public Builder(String name) { + this.name = name; + } + + private Builder() { } + + public Builder setName(String name) { + this.name = name; + return this; + } + + public Builder setIndex(int index) { + this.index = index; + return this; + } + + public Builder setVariable(String variable) { + this.variable = variable; + return this; + } + + public Builder setState(Set state) { + this.state = state; + return this; + } + + public Builder setEbnfList(boolean ebnfList) { + this.ebnfList = ebnfList; + return this; + } + + public Builder addParameters(String... parameters) { + addParameters(Arrays.asList(parameters)); + return this; + } + + public Builder addParameters(List parameters) { + if (parameters.isEmpty()) return this; + if (this.parameters == null) { + this.parameters = new ArrayList<>(parameters); + } else { + for (String parameter : parameters) { + if (!this.parameters.contains(parameter)) + this.parameters.add(parameter); + } + } + return this; + } + + public Builder apply(Expression... arguments) { + if (arguments.length == 0) + return this; + + if (this.arguments == null) { + this.arguments = arguments; + return this; + } + + Expression[] args = new Expression[this.arguments.length + arguments.length]; + int i = 0; + for (Expression argument : this.arguments) + args[i++] = argument; + + for (Expression argument : arguments) + args[i++] = argument; + + this.arguments = args; + return this; + } + + @Override + public Builder setLabel(String label) { + super.setLabel(label); + return this; + } + + @Override + public Builder addPreCondition(Condition condition) { + preConditions.add(condition); + return this; + } + + @Override + public Builder addPostCondition(Condition condition) { + postConditions.add(condition); + return this; + } + + @Override + public Builder setObject(Object object) { + this.object = object; + return this; + } + + @Override + public Builder addPreConditions(Iterable conditions) { + conditions.forEach(c -> preConditions.add(c)); + return this; + } + + @Override + public Builder addPostConditions(Iterable conditions) { + conditions.forEach(c -> postConditions.add(c)); + return this; + } + + public Builder addExcept(String label) { + if (excepts == null) excepts = new HashSet<>(); + excepts.add(label); + return this; + } + + public Builder addExcepts(Set labels) { + if (labels == null || labels.isEmpty()) + return this; + + if (excepts == null) + excepts = new HashSet<>(); + + excepts.addAll(labels); + return this; + } + + public Builder setNodeType(NonterminalNodeType nodeType) { + this.nodeType = nodeType; + return this; + } + + public Builder setAttributes(Map attributes) { + this.attributes = attributes; + return this; + } + + public Builder addAttribute(String key, Object value) { + this.attributes.put(key, value); + return this; + } + + public Builder addAttributes(Map attributes) { + this.attributes.putAll(attributes); + return this; + } + + @Override + public Nonterminal build() { + return new Nonterminal(this); + } + } + + @Override + public T accept(ISymbolVisitor visitor) { + return visitor.visit(this); + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/grammar/symbol/Offside.java b/benchmarks/src/main/kotlin/org/iguana/grammar/symbol/Offside.java new file mode 100644 index 000000000..7655c6364 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/grammar/symbol/Offside.java @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.grammar.symbol; + +import org.iguana.traversal.ISymbolVisitor; + +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +public class Offside extends AbstractSymbol { + + private final Symbol symbol; + + Offside(Builder builder) { + super(builder); + this.symbol = builder.symbol; + } + + public static Offside offside(Symbol symbol) { + return new Builder(symbol).build(); + } + + public Symbol getSymbol() { + return symbol; + } + + @Override + public Builder copy() { + return new Builder(this); + } + + @Override + public List getChildren() { + return Collections.singletonList(symbol); + } + + @Override + public int size() { + return symbol.size(); + } + + @Override + public String toString() { + return super.toString(); + } + + @Override + public String toString(int j) { + return String.format("offside %s", symbol.toString(j)); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Offside)) return false; + Offside offside = (Offside) o; + return Objects.equals(symbol, offside.symbol); + } + + @Override + public int hashCode() { + return Objects.hash(symbol); + } + + public static class Builder extends SymbolBuilder { + + private Symbol symbol; + + public Builder() { } + + public Builder(Offside offside) { + super(offside); + this.symbol = offside.symbol; + } + + public Builder(Symbol symbol) { + this.symbol = symbol; + } + + @Override + public SymbolBuilder setChildren(List symbols) { + this.symbol = symbols.get(0); + return this; + } + + @Override + public Offside build() { + this.name = String.format("offside %s", symbol.toString()); + return new Offside(this); + } + } + + @Override + public T accept(ISymbolVisitor visitor) { + return visitor.visit(this); + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/grammar/symbol/Opt.java b/benchmarks/src/main/kotlin/org/iguana/grammar/symbol/Opt.java new file mode 100644 index 000000000..2ce20f8fb --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/grammar/symbol/Opt.java @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.grammar.symbol; + +import org.iguana.traversal.ISymbolVisitor; + +import java.util.Collections; +import java.util.List; + +public class Opt extends AbstractSymbol { + + private final Symbol s; + + private Opt(Builder builder) { + super(builder); + this.s = builder.s; + } + + public static Opt from(Symbol s) { + return new Builder(s).build(); + } + + public Symbol getSymbol() { + return s; + } + + @Override + public Builder copy() { + return new Builder(this); + } + + @Override + public List getChildren() { + return Collections.singletonList(s); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + + if (!(obj instanceof Opt)) + return false; + + Opt other = (Opt) obj; + return s.equals(other.s); + } + + @Override + public int hashCode() { + return name.hashCode(); + } + + public static class Builder extends SymbolBuilder { + + private Symbol s; + + private Builder() {} + + public Builder(Symbol s) { + this.s = s; + } + + public Builder(Opt opt) { + super(opt); + this.s = opt.s; + } + + @Override + public SymbolBuilder setChildren(List symbols) { + this.s = symbols.get(0); + return this; + } + + @Override + public Opt build() { + this.name = s.getName() + "?"; + return new Opt(this); + } + } + + @Override + public T accept(ISymbolVisitor visitor) { + return visitor.visit(this); + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/grammar/symbol/Plus.java b/benchmarks/src/main/kotlin/org/iguana/grammar/symbol/Plus.java new file mode 100644 index 000000000..64608e9db --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/grammar/symbol/Plus.java @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.grammar.symbol; + +import org.iguana.traversal.ISymbolVisitor; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import static org.iguana.utils.string.StringUtil.listToString; + +public class Plus extends AbstractSymbol { + + private final Symbol s; + + private final List separators; + + private Plus(Builder builder) { + super(builder); + this.s = builder.s; + this.separators = Collections.unmodifiableList(builder.separators); + } + + public static Plus from(Symbol s) { + return new Builder(s).build(); + } + + public List getSeparators() { + return separators; + } + + @Override + public SymbolBuilder copy() { + return new Builder(this); + } + + @Override + public List getChildren() { + List children = new ArrayList<>(); + children.add(s); + children.addAll(separators); + return children; + } + + public Symbol getSymbol() { + return s; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + + if (!(obj instanceof Plus)) + return false; + + Plus other = (Plus) obj; + return s.equals(other.s) && separators.equals(other.separators); + } + + @Override + public int hashCode() { + return name.hashCode(); + } + + public static class Builder extends SymbolBuilder { + + private Symbol s; + + private List separators = new ArrayList<>(); + + private Builder() {} + + public Builder(Symbol s) { + this.s = s; + } + + public Builder(Plus plus) { + super(plus); + this.s = plus.s; + this.addSeparators(plus.getSeparators()); + } + + public Builder addSeparator(Symbol symbol) { + separators.add(symbol); + return this; + } + + public Builder addSeparators(List symbols) { + separators.addAll(symbols); + return this; + } + + public Builder addSeparators(Symbol... symbols) { + separators.addAll(Arrays.asList(symbols)); + return this; + } + + @Override + public SymbolBuilder setChildren(List symbols) { + this.s = symbols.get(0); + this.separators = symbols.subList(1, symbols.size()); + return this; + } + + @Override + public Plus build() { + if (separators.isEmpty()) { + this.name = s.getName() + "+"; + } else { + this.name = String.format("{%s %s}+", s.getName(), listToString(separators)); + } + return new Plus(this); + } + } + + @Override + public T accept(ISymbolVisitor visitor) { + return visitor.visit(this); + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/grammar/symbol/PriorityLevel.java b/benchmarks/src/main/kotlin/org/iguana/grammar/symbol/PriorityLevel.java new file mode 100644 index 000000000..0b0c386c4 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/grammar/symbol/PriorityLevel.java @@ -0,0 +1,69 @@ +package org.iguana.grammar.symbol; + +import java.util.ArrayList; +import java.util.List; + +/** + * A priority group is a list of alternatives. + */ +public class PriorityLevel { + + private final List alternatives; + + public PriorityLevel(Builder builder) { + this.alternatives = builder.alternatives; + } + + public static PriorityLevel from(Alternative... alternatives) { + return new PriorityLevel.Builder().addAlternatives(List.of(alternatives)).build(); + } + + public List getAlternatives() { + return alternatives; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (!(obj instanceof PriorityLevel)) return false; + PriorityLevel other = (PriorityLevel) obj; + return this.alternatives.equals(other.alternatives); + } + + @Override + public int hashCode() { + return alternatives.hashCode(); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + for (Alternative alternative : alternatives) { + sb.append(alternative); + sb.append("\n | "); + } + sb.delete(sb.length() - 5, sb.length()); + return sb.toString(); + } + + public static class Builder { + private List alternatives = new ArrayList<>(); + + public Builder addAlternative(Alternative alternative) { + alternatives.add(alternative); + return this; + } + + public Builder addAlternatives(List alternatives) { + this.alternatives.addAll(alternatives); + return this; + } + + public PriorityLevel build() { + if (alternatives.isEmpty()) { + throw new RuntimeException("Alternatives cannot be empty"); + } + return new PriorityLevel(this); + } + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/grammar/symbol/Return.java b/benchmarks/src/main/kotlin/org/iguana/grammar/symbol/Return.java new file mode 100644 index 000000000..5b4911893 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/grammar/symbol/Return.java @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.grammar.symbol; + +import org.iguana.datadependent.ast.Expression; +import org.iguana.traversal.ISymbolVisitor; + +public class Return extends AbstractSymbol { + + private final Expression expression; + + public Return(Builder builder) { + super(builder); + this.expression = builder.expression; + } + + public static Return ret(Expression expression) { + return new Builder(expression).build(); + } + + public Expression getExpression() { + return expression; + } + + @Override + public Builder copy() { + return new Builder(this); + } + + @Override + public String toString() { + return String.format("{%s}", expression.toString()); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (!(obj instanceof Return)) return false; + Return other = (Return) obj; + return this.expression.equals(other.expression); + } + + @Override + public int hashCode() { + return this.expression.hashCode(); + } + + public static class Builder extends SymbolBuilder { + + private Expression expression; + + private Builder() {} + + public Builder(Return ret) { + super(ret); + this.expression = ret.expression; + } + + public Builder(Expression expression) { + this.expression = expression; + } + + @Override + public Return build() { + this.name = String.format("{%s}", expression.toString()); + return new Return(this); + } + } + + @Override + public T accept(ISymbolVisitor visitor) { + return visitor.visit(this); + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/grammar/symbol/Rule.java b/benchmarks/src/main/kotlin/org/iguana/grammar/symbol/Rule.java new file mode 100644 index 000000000..b02bc6429 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/grammar/symbol/Rule.java @@ -0,0 +1,123 @@ +package org.iguana.grammar.symbol; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * A rule is comprised of a list of priority levels, which are separated by > in the textual grammar notation. + * In the following example, there are two priority groups, one consisting of the alternative A B | C D, + * and second of the alternative E F | G H. + * + * S : A B + * | C D + * > E F + * | G H + * ; + */ +public class Rule { + + private final Nonterminal head; + + private final List priorityLevels; + + private final LayoutStrategy layoutStrategy; + + public Rule(Builder builder) { + this.head = builder.head; + this.priorityLevels = builder.priorityLevels; + this.layoutStrategy = builder.layoutStrategy; + } + + public Nonterminal getHead() { + return head; + } + + public List getPriorityLevels() { + return priorityLevels; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (!(obj instanceof Rule)) return false; + Rule other = (Rule) obj; + return this.head.equals(other.head) && this.priorityLevels.equals(other.priorityLevels); + } + + @Override + public int hashCode() { + return Objects.hash(head, priorityLevels); + } + + public LayoutStrategy getLayoutStrategy() { + return layoutStrategy; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + if (head.toString().length() == 1) { + sb.append(head).append(" = "); + } else { + sb.append(head).append("\n = "); + } + if (priorityLevels.isEmpty()) { + sb.append("\n"); + } else { + for (PriorityLevel priorityLevel : priorityLevels) { + sb.append(priorityLevel).append("\n > "); + } + sb.delete(sb.length() - 5, sb.length()); + sb.append("\n"); + } + return sb.toString(); + } + + public Builder copy() { + return new Builder(this); + } + + public static class Builder { + private Nonterminal head; + private List priorityLevels = new ArrayList<>(); + private LayoutStrategy layoutStrategy = LayoutStrategy.INHERITED; + + private Builder() { } + + public Builder(Rule rule) { + this.head = rule.head; + this.priorityLevels = new ArrayList<>(rule.priorityLevels); + this.layoutStrategy = rule.layoutStrategy; + } + + public Builder(Nonterminal head) { + this.head = head; + } + + public Builder addPriorityLevel(PriorityLevel priorityLevel) { + priorityLevels.add(priorityLevel); + return this; + } + + public Builder addPriorityLevels(List priorityLevels) { + this.priorityLevels.addAll(priorityLevels); + return this; + } + + public Builder clearPriorityLevels() { + this.priorityLevels.clear(); + return this; + } + + public Builder setLayoutStrategy(LayoutStrategy layoutStrategy) { + this.layoutStrategy = layoutStrategy; + return this; + } + + public Rule build() { + return new Rule(this); + } + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/grammar/symbol/Sequence.java b/benchmarks/src/main/kotlin/org/iguana/grammar/symbol/Sequence.java new file mode 100644 index 000000000..b458f0f29 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/grammar/symbol/Sequence.java @@ -0,0 +1,160 @@ +package org.iguana.grammar.symbol; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import static org.iguana.utils.collections.CollectionsUtil.buildList; +import static org.iguana.utils.collections.CollectionsUtil.buildMap; + +public class Sequence { + + private final List symbols; + + private final Associativity associativity; + + private final String label; + + private final Map attributes; + + public Sequence(Builder builder) { + this.symbols = builder.symbols; + this.associativity = builder.associativity; + this.label = builder.label; + this.attributes = builder.attributes; + } + + public static Sequence from(Symbol... symbols) { + return new Builder().addSymbols(List.of(symbols)).build(); + } + + public boolean isEmpty() { + return symbols.isEmpty(); + } + + + public Symbol first() { + if (symbols.isEmpty()) return null; + return symbols.get(0); + } + + public List rest() { + if (symbols.isEmpty() || symbols.size() == 1) return null; + return symbols.subList(1, symbols.size()); + } + + public List getSymbols() { + return symbols; + } + + public Associativity getAssociativity() { + return associativity; + } + + public String getLabel() { + return label; + } + + public Map getAttributes() { + return attributes; + } + + public Builder copy() { + return new Builder(this); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (!(obj instanceof Sequence)) return false; + Sequence other = (Sequence) obj; + return Objects.equals(this.symbols, other.symbols) + && Objects.equals(this.associativity, other.associativity) + && Objects.equals(this.label, other.label); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + List symbols = getSymbols(); + if (associativity != null && associativity != Associativity.UNDEFINED) { + sb.append(associativity).append(" "); + } + for (Symbol symbol : symbols) { + sb.append(symbol).append(" "); + } + if (sb.length() > 0) { + sb.deleteCharAt(sb.length() - 1); + } + if (label != null) { + sb.append(" ").append("%").append(label); + } + if (!attributes.isEmpty()) { + sb.append(" {"); + for (Map.Entry entry : attributes.entrySet()) { + if (entry.getValue() == null) { + sb.append(String.format("%s ", entry.getKey())); + } else { + sb.append(String.format("%s=%s ", entry.getKey(), entry.getValue())); + } + } + sb.deleteCharAt(sb.length() - 1); + sb.append("}"); + } + return sb.toString(); + } + + public static class Builder { + private Map attributes = new HashMap<>(); + private List symbols = new ArrayList<>(); + private Associativity associativity = Associativity.UNDEFINED; + private String label; + + public Builder() {} + + public Builder(Sequence seq) { + this.attributes = new HashMap<>(seq.getAttributes()); + this.symbols = new ArrayList<>(seq.symbols); + this.associativity = seq.associativity; + this.label = seq.label; + } + + public Builder addSymbol(Symbol symbol) { + this.symbols.add(symbol); + return this; + } + + public Builder clearSymbols() { + this.symbols.clear(); + return this; + } + + public Builder addSymbols(List symbols) { + this.symbols.addAll(symbols); + return this; + } + + public Builder setAssociativity(Associativity associativity) { + this.associativity = associativity; + return this; + } + + public Builder setLabel(String label) { + this.label = label; + return this; + } + + public Builder addAttribute(String key, Object value) { + this.attributes.put(key, value); + return this; + } + + public Sequence build() { + attributes = buildMap(attributes); + symbols = buildList(symbols); + return new Sequence(this); + } + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/grammar/symbol/Star.java b/benchmarks/src/main/kotlin/org/iguana/grammar/symbol/Star.java new file mode 100644 index 000000000..188a0d11e --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/grammar/symbol/Star.java @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.grammar.symbol; + +import org.iguana.traversal.ISymbolVisitor; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import static org.iguana.utils.string.StringUtil.listToString; + +public class Star extends AbstractSymbol { + + private final Symbol s; + + private final List separators; + + private Star(Builder builder) { + super(builder); + this.s = builder.s; + this.separators = Collections.unmodifiableList(builder.separators); + } + + public static Star from(Symbol s) { + return new Builder(s).build(); + } + + public List getSeparators() { + return separators; + } + + @Override + public Builder copy() { + return new Builder(this); + } + + @Override + public List getChildren() { + List children = new ArrayList<>(); + children.add(s); + children.addAll(separators); + return children; + } + + public Symbol getSymbol() { + return s; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + + if (!(obj instanceof Star)) + return false; + + Star other = (Star) obj; + return s.equals(other.s) && separators.equals(other.separators); + } + + @Override + public int hashCode() { + return name.hashCode(); + } + + public static class Builder extends SymbolBuilder { + + private Symbol s; + private List separators = new ArrayList<>(); + + private Builder() {} + + public Builder(Symbol s) { + this.s = s; + } + + public Builder(Star star) { + super(star); + this.s = star.s; + this.addSeparators(star.getSeparators()); + } + + public Builder addSeparator(Symbol symbol) { + separators.add(symbol); + return this; + } + + public Builder addSeparators(List symbols) { + separators.addAll(symbols); + return this; + } + + public Builder addSeparators(Symbol... symbols) { + separators.addAll(Arrays.asList(symbols)); + return this; + } + + @Override + public SymbolBuilder setChildren(List symbols) { + this.s = symbols.get(0); + this.separators = symbols.subList(1, symbols.size()); + return this; + } + + @Override + public Star build() { + if (separators.isEmpty()) { + this.name = s.getName() + "*"; + } else { + this.name = String.format("{%s %s}*", s.getName(), listToString(separators)); + } + return new Star(this); + } + } + + @Override + public T accept(ISymbolVisitor visitor) { + return visitor.visit(this); + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/grammar/symbol/Start.java b/benchmarks/src/main/kotlin/org/iguana/grammar/symbol/Start.java new file mode 100644 index 000000000..3e59fc17f --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/grammar/symbol/Start.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.grammar.symbol; + + +import org.iguana.traversal.ISymbolVisitor; + +public class Start extends AbstractSymbol { + + private final String startSymbol; + + public Start(Builder builder) { + super(builder); + this.startSymbol = builder.startSymbol; + } + + public static Start from(String startSymbol) { + return new Builder(startSymbol).build(); + } + + public String getStartSymbol() { + return startSymbol; + } + + @Override + public Builder copy() { + return new Builder(startSymbol); + } + + @Override + public T accept(ISymbolVisitor visitor) { + return visitor.visit(this); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (!(obj instanceof Start)) return false; + Start other = (Start) obj; + return this.name.equals(other.name); + } + + @Override + public int hashCode() { + return name.hashCode(); + } + + public static class Builder extends SymbolBuilder { + + private String startSymbol; + + private Builder() {} + + public Builder(String startSymbol) { + this.startSymbol = startSymbol; + } + + @Override + public Start build() { + if (startSymbol == null) + throw new IllegalArgumentException("startSymbol cannot be null."); + this.name = "Start(" + startSymbol + ")"; + return new Start(this); + } + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/grammar/symbol/Symbol.java b/benchmarks/src/main/kotlin/org/iguana/grammar/symbol/Symbol.java new file mode 100644 index 000000000..23867d6b9 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/grammar/symbol/Symbol.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.grammar.symbol; + +import org.iguana.datadependent.attrs.Attr; +import org.iguana.grammar.condition.Condition; +import org.iguana.traversal.ISymbolVisitor; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +/** + * + * + * @author Ali Afroozeh + * @author Anastasia Izmaylova + * + *
+ * Symbol ::= Label ':' Symbol
+ *          | Nonterminal '(' {Expression ','}+ ')'
+ *          | ...
+ *          | '{' Symbol+ '}'
+ *       > "align" Symbol
+ *          | "offside" Symbol
+ *       > Symbol "do" Statement
+ *          | Symbol "when" Expression
+ *       > "if" '(' Expression ')' Symbol
+ *          | "if" '(' Expression ')' Symbol "else" Symbol
+ *          | "while" '(' Expression ')' Symbol
+ * 
+ * + */ +public interface Symbol extends Attr { + + String getName(); + + List getPreConditions(); + + List getPostConditions(); + + String getLabel(); + + SymbolBuilder copy(); + + default List getChildren() { + return Collections.emptyList(); + } + + default int size() { + return 1; + } + + String toString(int j); + + Map getAttributes(); + + T accept(ISymbolVisitor visitor); +} diff --git a/benchmarks/src/main/kotlin/org/iguana/grammar/symbol/SymbolBuilder.java b/benchmarks/src/main/kotlin/org/iguana/grammar/symbol/SymbolBuilder.java new file mode 100644 index 000000000..de9edafcb --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/grammar/symbol/SymbolBuilder.java @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.grammar.symbol; + +import org.iguana.grammar.condition.Condition; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public abstract class SymbolBuilder { + + protected String name; + + protected String label; + + protected Object object; + + protected List preConditions = new ArrayList<>(); + + protected List postConditions = new ArrayList<>(); + + protected Map attributes = new HashMap<>(); + + public SymbolBuilder(T symbol) { + this.name = symbol.getName(); + this.label = symbol.getLabel(); + this.preConditions = new ArrayList<>(symbol.getPreConditions()); + this.postConditions = new ArrayList<>(symbol.getPostConditions()); + this.attributes = new HashMap<>(symbol.getAttributes()); + } + + public SymbolBuilder() { } + + public SymbolBuilder setLabel(String label) { + this.label = label; + return this; + } + + public SymbolBuilder setName(String name) { + this.name = name; + return this; + } + + public SymbolBuilder addPreCondition(Condition condition) { + preConditions.add(condition); + return this; + } + + public SymbolBuilder addPostCondition(Condition condition) { + postConditions.add(condition); + return this; + } + + public SymbolBuilder setObject(Object object) { + this.object = object; + return this; + } + + public SymbolBuilder addConditions(Symbol s) { + addPreConditions(s.getPreConditions()); + addPostConditions(s.getPostConditions()); + return this; + } + + public SymbolBuilder addPreConditions(Iterable conditions) { + conditions.forEach(c -> preConditions.add(c)); + return this; + } + + // TODO: Get rid of this method, use the addCondition method instead + public SymbolBuilder addPostConditions(Iterable conditions) { + conditions.forEach(c -> postConditions.add(c)); + return this; + } + + public SymbolBuilder removePreConditions(Collection conditions) { + preConditions.removeAll(conditions); + return this; + } + + public SymbolBuilder removePostConditions(Collection conditions) { + postConditions.removeAll(conditions); + return this; + } + + public SymbolBuilder setPreConditions(List conditions) { + preConditions = new ArrayList<>(conditions); + return this; + } + + public SymbolBuilder setPostConditions(List conditions) { + postConditions = new ArrayList<>(conditions); + return this; + } + + public SymbolBuilder addAttribute(String key, Object value) { + this.attributes.put(key, value); + return this; + } + + public SymbolBuilder setChildren(List symbols) { + return this; + } + + public abstract T build(); + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/grammar/symbol/Terminal.java b/benchmarks/src/main/kotlin/org/iguana/grammar/symbol/Terminal.java new file mode 100644 index 000000000..1eacd7248 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/grammar/symbol/Terminal.java @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.grammar.symbol; + +import org.iguana.grammar.slot.TerminalNodeType; +import org.iguana.regex.Epsilon; +import org.iguana.regex.RegularExpression; +import org.iguana.traversal.ISymbolVisitor; + +public class Terminal extends AbstractSymbol { + + private static final Terminal epsilon = Terminal.from(Epsilon.getInstance()); + + private final TerminalNodeType nodeType; + + private final RegularExpression regex; + + public Terminal(Builder builder) { + super(builder); + this.regex = builder.regex; + this.nodeType = builder.nodeType; + } + + public static Terminal epsilon() { + return epsilon; + } + + public static Terminal from(RegularExpression regex) { + return new Builder(regex).build(); + } + + + @Override + public Builder copy() { + return new Builder(this); + } + + public RegularExpression getRegularExpression() { + return regex; + } + + public TerminalNodeType getNodeType() { + return nodeType; + } + + @Override + public int hashCode() { + return regex.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) + return true; + + if (!(obj instanceof Terminal)) + return false; + + Terminal other = (Terminal) obj; + + return regex.equals(other.regex); + } + + public static class Builder extends SymbolBuilder { + + private TerminalNodeType nodeType; + private RegularExpression regex; + + public Builder(RegularExpression regex) { + this.regex = regex; + } + + private Builder() {} + + public Builder(Terminal terminal) { + super(terminal); + this.regex = terminal.regex; + this.nodeType = terminal.getNodeType(); + } + + public Builder setNodeType(TerminalNodeType nodeType) { + this.nodeType = nodeType; + return this; + } + + @Override + public Terminal build() { + if (name == null) + name = regex.toString(); + return new Terminal(this); + } + } + + public boolean isNullable() { + return regex.isNullable(); + } + + @Override + public String toString() { + return regex.toString(); + } + + @Override + public T accept(ISymbolVisitor visitor) { + return visitor.visit(this); + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/grammar/symbol/While.java b/benchmarks/src/main/kotlin/org/iguana/grammar/symbol/While.java new file mode 100644 index 000000000..734e93992 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/grammar/symbol/While.java @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.grammar.symbol; + +import org.iguana.datadependent.ast.Expression; +import org.iguana.traversal.ISymbolVisitor; + +import java.util.Collections; +import java.util.List; + +public class While extends AbstractSymbol { + + private final Expression expression; + private final Symbol body; + + While(Builder builder) { + super(builder); + this.expression = builder.expression; + this.body = builder.body; + } + + public static While whileLoop(Expression expression, Symbol body) { + return new Builder(expression, body).build(); + } + + public Expression getExpression() { + return expression; + } + + public Symbol getBody() { + return body; + } + + @Override + public Builder copy() { + return new Builder(this); + } + + @Override + public List getChildren() { + return Collections.singletonList(body); + } + + @Override + public int size() { + return body.size(); + } + + @Override + public String toString() { + return String.format("while (%s) %s", expression.toString(), body.toString()); + } + + @Override + public String toString(int j) { + return String.format("while (%s) %s", expression.toString(), body.toString(j)); + } + + public static class Builder extends SymbolBuilder { + + private Expression expression; + private Symbol body; + + public Builder(While whileSymbol) { + super(whileSymbol); + this.expression = whileSymbol.expression; + this.body = whileSymbol.body; + } + + public Builder(Expression expression, Symbol body) { + this.expression = expression; + this.body = body; + } + + @Override + public SymbolBuilder setChildren(List symbols) { + this.body = symbols.get(0); + return this; + } + + @Override + public While build() { + this.name = String.format("while (%s) %s", expression.toString(), body.toString()); + return new While(this); + } + } + + @Override + public T accept(ISymbolVisitor visitor) { + return visitor.visit(this); + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/grammar/transformation/DesugarAlignAndOffside.java b/benchmarks/src/main/kotlin/org/iguana/grammar/transformation/DesugarAlignAndOffside.java new file mode 100644 index 000000000..130c31a8b --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/grammar/transformation/DesugarAlignAndOffside.java @@ -0,0 +1,750 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.grammar.transformation; + +import org.iguana.datadependent.ast.Expression; +import org.iguana.grammar.condition.Condition; +import org.iguana.grammar.operations.ReachabilityGraph; +import org.iguana.grammar.runtime.RuntimeGrammar; +import org.iguana.grammar.runtime.RuntimeRule; +import org.iguana.grammar.symbol.Align; +import org.iguana.grammar.symbol.Alt; +import org.iguana.grammar.symbol.Block; +import org.iguana.grammar.symbol.Code; +import org.iguana.grammar.symbol.Conditional; +import org.iguana.grammar.symbol.Error; +import org.iguana.grammar.symbol.Group; +import org.iguana.grammar.symbol.IfThen; +import org.iguana.grammar.symbol.IfThenElse; +import org.iguana.grammar.symbol.Ignore; +import org.iguana.grammar.symbol.Nonterminal; +import org.iguana.grammar.symbol.Offside; +import org.iguana.grammar.symbol.Opt; +import org.iguana.grammar.symbol.Plus; +import org.iguana.grammar.symbol.Return; +import org.iguana.grammar.symbol.Star; +import org.iguana.grammar.symbol.Start; +import org.iguana.grammar.symbol.Symbol; +import org.iguana.grammar.symbol.Terminal; +import org.iguana.grammar.symbol.While; +import org.iguana.traversal.ISymbolVisitor; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static org.iguana.datadependent.ast.AST.andIndent; +import static org.iguana.datadependent.ast.AST.equal; +import static org.iguana.datadependent.ast.AST.indent; +import static org.iguana.datadependent.ast.AST.integer; +import static org.iguana.datadependent.ast.AST.lExt; +import static org.iguana.datadependent.ast.AST.orIndent; +import static org.iguana.datadependent.ast.AST.var; +import static org.iguana.grammar.condition.DataDependentCondition.predicate; + +public class DesugarAlignAndOffside implements GrammarTransformation { + + private Map> reachabilityGraph; + + private Set offsided; + + private boolean doAlign = true; + + public void doAlign() { + this.doAlign = true; + } + + public void doOffside() { + this.doAlign = false; + } + + @Override + public RuntimeGrammar transform(RuntimeGrammar grammar) { + + if (doAlign) { + DesugarAlignAndOffsideVisitor desugarAligns = new DesugarAlignAndOffsideVisitor(new HashSet<>()); + desugarAligns.doAlign(doAlign); + + Set rules = new LinkedHashSet<>(); + + for (RuntimeRule rule : grammar.getRules()) + rules.add(desugarAligns.transform(rule, grammar.getLayout())); + + return RuntimeGrammar.builder().addRules(rules) + .setLayout(grammar.getLayout()) + .setStartSymbols(grammar.getStartSymbols()) + .setEbnfLefts(grammar.getEBNFLefts()) + .setEbnfRights(grammar.getEBNFRights()) + .setGlobals(grammar.getGlobals()) + .setRegularExpressionDefinitions(grammar.getRegularExpressionDefinitions()) + .build(); + } + + // After EBNF translation + reachabilityGraph = new ReachabilityGraph(grammar).getReachabilityGraph(); + + FindOffsidesVisitor findOffsides = new FindOffsidesVisitor(); + findOffsides.find(grammar); + offsided = findOffsides.getOffsides(); + + reachabilityGraph.entrySet().forEach(e -> { + if (offsided.contains(e.getKey().getName())) { + e.getValue().forEach(n -> offsided.add(n.getName())); + } + }); + + DesugarAlignAndOffsideVisitor desugarOffsides = new DesugarAlignAndOffsideVisitor(offsided); + desugarOffsides.doAlign(doAlign); + + Set rules = new LinkedHashSet<>(); + + for (RuntimeRule rule : grammar.getRules()) + rules.add(desugarOffsides.transform(rule, grammar.getLayout())); + + return RuntimeGrammar.builder().addRules(rules) + .setLayout(grammar.getLayout()) + .setStartSymbols(grammar.getStartSymbols()) + .setGlobals(grammar.getGlobals()) + .setEbnfLefts(grammar.getEBNFLefts()) + .setEbnfRights(grammar.getEBNFRights()) + .setRegularExpressionDefinitions(grammar.getRegularExpressionDefinitions()) + .build(); + } + + private static class DesugarAlignAndOffsideVisitor implements ISymbolVisitor { + + private static final String ind = "ind"; + private static final String first = "fst"; + private static final String index = "i"; + + private static final Expression ind_exp = var(ind); + private static final Expression first_exp = var(first); + private static final Expression index_exp = var(index); + + private static final String l_align = "a"; + private static final String l_offside = "o"; + + private boolean doAlign; + + private final Set offsided; + + private RuntimeRule rule; + private Symbol layout; + + private boolean isOffsided; + + private int i; + + DesugarAlignAndOffsideVisitor(Set offsided) { + this.offsided = offsided; + } + + public void doAlign(boolean doAlign) { + this.doAlign = doAlign; + } + + public RuntimeRule transform(RuntimeRule rule, Symbol layout) { + this.rule = rule; + this.layout = layout; + this.isOffsided = false; + i = 0; + + if (doAlign) { + List symbols = new ArrayList<>(); + RuntimeRule.Builder builder = rule.copy(); + + if (this.rule.getBody() != null) { + + builder = builder.setSymbols(symbols); + + for (Symbol symbol : this.rule.getBody()) { +// if (symbol instanceof Align) { +// Symbol sym = ((Align) symbol).getSymbol(); +// if (sym instanceof Plus || sym instanceof Star || sym instanceof Sequence) { +// Symbol s = symbol.accept(this); +// String l3 = l_align + i++; +// Nonterminal longest = getLayout().copyBuilder().setLabel(l3) +// .addPostCondition(predicate(or(endOfFile(rExt(l3)), +// lessEq(indent(rExt(l3)), indent(lExt(s.getLabel())))))) +// .build(); +// symbols.add(s); +// symbols.add(longest); +// continue; +// } +// } + symbols.add(symbol.accept(this)); + } + } + + return builder.build(); + } + + isOffsided = offsided.contains(this.rule.getHead().getName()); + + RuntimeRule.Builder builder; + + if (isOffsided) + builder = rule.copyBuilderButWithHead(rule.getHead().copy().addParameters(index, ind, first).build()); + else + builder = rule.copy(); + + List symbols = new ArrayList<>(); + + if (this.rule.getBody() != null) { + + builder = builder.setSymbols(symbols); + + for (Symbol symbol : this.rule.getBody()) { + symbols.add(symbol.accept(this)); + } + } + + return builder.build(); + } + + @Override + public Symbol visit(Align symbol) { + + if (doAlign) { + Symbol sym = symbol.getSymbol().accept(this); + + if (sym instanceof Plus) { + String l1 = getLabel(symbol, sym); + + Plus plus = (Plus) sym; + + Symbol s = plus.getSymbol(); + String l2 = s.getLabel() != null ? s.getLabel() : l_align + i++; // TODO: conflicting labels + + s = getSymbol(s, predicate(equal(indent(lExt(l2)), indent(lExt(l1)))), l2); + + return new Plus.Builder(s).addSeparators(plus.getSeparators()).setLabel(l1).addConditions(plus) + .addConditions(symbol).build(); + + } else if (sym instanceof Star) { + String l1 = getLabel(symbol, sym); + + Star star = (Star) sym; + + Symbol s = star.getSymbol(); + String l2 = s.getLabel() != null ? s.getLabel() : l_align + i++; // TODO: conflicting labels + + s = getSymbol(s, predicate(equal(indent(lExt(l2)), indent(lExt(l1)))), l2); + + return new Star.Builder(s).addSeparators(star.getSeparators()).setLabel(l1).addConditions(star) + .addConditions(symbol).build(); + } else if (sym instanceof Group) { + String l1 = getLabel(symbol, sym); + + @SuppressWarnings("unchecked") + Group seq = (Group) sym; + + List symbols = seq.getSymbols(); + List syms = new ArrayList<>(); + + for (Symbol s : symbols) { + String l2 = s.getLabel() != null ? s.getLabel() : l_align + i++; // TODO: conflicting labels + syms.add(getSymbol(s, predicate(equal(indent(lExt(l2)), indent(lExt(l1)))), l2)); + } + + return new Group.Builder(syms).setLabel(l1) + .addConditions(seq).addConditions(symbol).build(); + } else + return sym; + } + + Symbol sym = symbol.getSymbol().accept(this); + + return sym == symbol.getSymbol() ? symbol + : new Align.Builder(sym).setLabel(symbol.getLabel()).addConditions(symbol).build(); + } + + @Override + public Symbol visit(Block symbol) { + List symbols = symbol.getSymbols(); + Symbol[] syms = new Symbol[symbols.size()]; + + int j = 0; + boolean modified = false; + for (Symbol sym : symbols) { + + syms[j] = sym.accept(this); + if (sym != syms[j]) + modified |= true; + j++; + } + + return modified ? new Block.Builder(syms).setLabel(symbol.getLabel()).addConditions(symbol).build() + : symbol; + } + + @Override + public Symbol visit(Code symbol) { + Symbol sym = symbol.getSymbol().accept(this); + if (sym == symbol.getSymbol()) + return symbol; + + return new Code.Builder(sym, symbol.getStatements()).setLabel(symbol.getLabel()).addConditions(symbol) + .build(); + } + + @Override + public Symbol visit(Error error) { + return error; + } + + @Override + public Symbol visit(Conditional symbol) { + Symbol sym = symbol.getSymbol().accept(this); + if (sym == symbol.getSymbol()) + return symbol; + + return new Conditional.Builder(sym, symbol.getExpression()).setLabel(symbol.getLabel()) + .addConditions(symbol).build(); + } + + @Override + public Symbol visit(IfThen symbol) { + Symbol sym = symbol.getThenPart().accept(this); + if (sym == symbol.getThenPart()) + return symbol; + + return new IfThen.Builder(symbol.getExpression(), sym).setLabel(symbol.getLabel()).addConditions(symbol) + .build(); + } + + @Override + public Symbol visit(IfThenElse symbol) { + Symbol thenPart = symbol.getThenPart().accept(this); + Symbol elsePart = symbol.getElsePart().accept(this); + if (thenPart == symbol.getThenPart() + && elsePart == symbol.getElsePart()) + return symbol; + + return new IfThenElse.Builder(symbol.getExpression(), thenPart, elsePart).setLabel(symbol.getLabel()) + .addConditions(symbol).build(); + } + + @Override + public Symbol visit(Nonterminal symbol) { + // The rule has a parameter for indentation, and therefore, also all reachable nonterminals + if (isOffsided && offsided.contains(symbol.getName())) { + String l = symbol.getLabel() != null ? symbol.getLabel() : l_offside + i++; + return symbol.copy().apply(// (fst & (lExt - index == 0)) == 1? index : 0 or fst == 1? index : 0 + // after non-nullable (0 as only indentation will be needed) + andIndent(index_exp, first_exp, lExt(l), true), + ind_exp, + // fst & (lExt - index == 0) or 0 after non-nullable + andIndent(index_exp, first_exp, lExt(l))) + .setLabel(l).build(); + } else if (offsided.contains(symbol.getName())) // A ::= offside B; B ::= D; C ::= B or C ::= D + return symbol.copy().apply(integer(0), integer(0), integer(0)).build(); + else + return symbol; + } + + @Override + public Symbol visit(Ignore symbol) { + if (doAlign) { + Symbol sym = symbol.getSymbol().accept(this); + + return sym == symbol.getSymbol() ? symbol + : new Ignore.Builder(sym).setLabel(symbol.getLabel()).addConditions(symbol).build(); + } + + Symbol sym = symbol.getSymbol(); + + if (sym instanceof Nonterminal) { + Nonterminal s = (Nonterminal) sym; + + if (offsided.contains(s.getName())) { // TODO: too general + return s.copy() + .apply(integer(0), integer(0), integer(0)) + .addConditions(symbol) + .addConditions(sym) + .build(); + } + } + + // Otherwise, ignore 'ignore' + sym = sym.accept(this); + return sym.copy().addConditions(symbol).build(); + } + + @Override + public Symbol visit(Offside symbol) { + + if (doAlign) { + Symbol sym = symbol.getSymbol().accept(this); + + return sym == symbol.getSymbol() ? symbol + : new Offside.Builder(sym).setLabel(symbol.getLabel()).addConditions(symbol).build(); + } + + Symbol sym = symbol.getSymbol(); + + if (sym instanceof Nonterminal) { + Nonterminal s = (Nonterminal) sym; + + String l = symbol.getLabel(); + if (l != null && s.getLabel() != null) { + if (!l.equals(s.getLabel())) + throw new RuntimeException("Conflicting labels: " + symbol); + } else if (s.getLabel() != null) + l = s.getLabel(); + else if (l == null && s.getLabel() == null) + l = l_offside + i++; + + if (isOffsided) { // Offside inside a rule that has a parameter for indentation + return s.copy() + .apply(lExt(l), indent(lExt(l)), integer(1)) + .setLabel(l) + .addConditions(symbol) + .addConditions(sym) + // [ ind == 0 || (first && l.lExt - index == 0) || indent(l.lExt) > ind] + .addPreCondition(predicate(orIndent(index_exp, ind_exp, first_exp, lExt(l)))) + .build(); + } else { + return s.copy() + .apply(lExt(l), indent(lExt(l)), integer(1)) + .setLabel(l) + .addConditions(symbol) + .addConditions(sym) + .build(); + } + } + + // Otherwise, ignore offside + sym = sym.accept(this); + return sym.copy().addConditions(symbol).build(); + } + + @Override + public Symbol visit(Terminal symbol) { + if (isOffsided) { + String l = symbol.getLabel() != null ? symbol.getLabel() : l_offside + i++; + return symbol.copy() + .setLabel(l) + .addConditions(symbol) + .addPreCondition(predicate(orIndent(index_exp, ind_exp, first_exp, lExt(l)))) + .build(); + } + return symbol; + } + + @Override + public Symbol visit(While symbol) { + Symbol body = symbol.getBody().accept(this); + if (body == symbol.getBody()) + return symbol; + + return new While.Builder(symbol.getExpression(), body).setLabel(symbol.getLabel()).addConditions(symbol) + .build(); + } + + @Override + public Symbol visit(Return symbol) { + return symbol; + } + + @Override + public Symbol visit(Alt symbol) { + + List symbols = symbol.getSymbols(); + List syms = new ArrayList<>(); + + boolean modified = false; + for (Symbol sym : symbols) { + Symbol s = sym.accept(this); + syms.add(s); + if (sym != s) + modified |= true; + } + + return modified ? new Alt.Builder(syms).setLabel(symbol.getLabel()).addConditions(symbol).build() + : symbol; + } + + @Override + public Symbol visit(Opt symbol) { + Symbol sym = symbol.getSymbol().accept(this); + if (sym == symbol.getSymbol()) + return symbol; + return new Opt.Builder(sym).setLabel(symbol.getLabel()).addConditions(symbol).build(); + } + + @Override + public Symbol visit(Plus symbol) { + + Symbol sym = symbol.getSymbol().accept(this); + List separators = symbol.getSeparators(); + + boolean modified = sym != symbol.getSymbol(); + + List seps = new ArrayList<>(); + for (Symbol sep : separators) { + Symbol s = sep.accept(this); + seps.add(s); + if (s != sep) + modified |= true; + } + + return modified ? new Plus.Builder(sym).addSeparators(seps).setLabel(symbol.getLabel()) + .addConditions(symbol).build() + : symbol; + } + + @Override + public Symbol visit(Group symbol) { + + List symbols = symbol.getSymbols(); + List syms = new ArrayList<>(); + + boolean modified = false; + for (Symbol sym : symbols) { + Symbol s = sym.accept(this); + syms.add(s); + if (sym != s) + modified |= true; + } + + return modified ? new Group.Builder(syms).setLabel(symbol.getLabel()).addConditions(symbol).build() + : symbol; + } + + @Override + public Symbol visit(Star symbol) { + Symbol sym = symbol.getSymbol().accept(this); + List separators = symbol.getSeparators(); + + boolean modified = sym != symbol.getSymbol(); + + List seps = new ArrayList<>(); + for (Symbol sep : separators) { + Symbol s = sep.accept(this); + seps.add(s); + if (s != sep) + modified |= true; + } + + return modified ? new Star.Builder(sym).addSeparators(seps).setLabel(symbol.getLabel()) + .addConditions(symbol).build() + : symbol; + } + + @Override + public Symbol visit(Start start) { + return null; + } + + private String getLabel(Align align, Symbol symbol) { + String label = null; + + if (align.getLabel() != null && symbol.getLabel() != null) { + if (!align.getLabel().equals(symbol.getLabel())) + throw new RuntimeException("Two conflicting labels: " + align); + else + label = align.getLabel(); + } else if (align.getLabel() != null) + label = align.getLabel(); + else if (symbol.getLabel() != null) + label = symbol.getLabel(); + + return label != null ? label : l_align + i++; + } + + private Symbol getSymbol(Symbol symbol, Condition precondition, String label) { + + if (symbol instanceof Offside) { + Offside sym = (Offside) symbol; + return new Offside.Builder(sym.getSymbol().copy().addPreCondition(precondition).setLabel(label).build()) + .addConditions(symbol).setLabel(symbol.getLabel()).build(); + } + + return symbol.copy().addPreCondition(precondition).setLabel(label).build(); + } + + @SuppressWarnings("unused") + private Symbol getLayout() { + switch (rule.getLayoutStrategy()) { + case NO_LAYOUT: + throw new RuntimeException("Align should not be part of lexicals."); + case INHERITED: + return layout; + case FIXED: + return rule.getLayout(); + } + return layout; + } + + } + + private static class FindOffsidesVisitor implements ISymbolVisitor { + + private final Set offsided; + + FindOffsidesVisitor() { + this.offsided = new HashSet<>(); + } + + public Set getOffsides() { + return offsided; + } + + public void find(RuntimeGrammar grammar) { + + for (RuntimeRule rule : grammar.getRules()) { + if (rule.getBody() == null) + continue; + + for (Symbol s : rule.getBody()) + s.accept(this); + } + } + + @Override + public Void visit(Offside symbol) { + Symbol sym = symbol.getSymbol(); + + // Offside will only be applied to nonterminals + if (sym instanceof Nonterminal) + offsided.add(((Nonterminal) sym).getName()); + + return sym.accept(this); + } + + @Override + public Void visit(Align symbol) { + return symbol.getSymbol().accept(this); + } + + @Override + public Void visit(Block symbol) { + for (Symbol sym : symbol.getSymbols()) + sym.accept(this); + return null; + } + + @Override + public Void visit(Code symbol) { + return symbol.getSymbol().accept(this); + } + + @Override + public Void visit(Error error) { + return null; + } + + @Override + public Void visit(Conditional symbol) { + return symbol.getSymbol().accept(this); + } + + @Override + public Void visit(IfThen symbol) { + return symbol.getThenPart().accept(this); + } + + @Override + public Void visit(IfThenElse symbol) { + symbol.getThenPart().accept(this); + symbol.getElsePart().accept(this); + return null; + } + + @Override + public Void visit(Ignore symbol) { + return symbol.getSymbol().accept(this); + } + + @Override + public Void visit(Nonterminal symbol) { + return null; + } + + @Override + public Void visit(Terminal symbol) { + return null; + } + + @Override + public Void visit(While symbol) { + return symbol.getBody().accept(this); + } + + @Override + public Void visit(Return symbol) { + // TODO: support for return + return null; + } + + @Override + public Void visit(Alt symbol) { + for (Symbol sym : symbol.getSymbols()) + sym.accept(this); + return null; + } + + @Override + public Void visit(Opt symbol) { + return symbol.getSymbol().accept(this); + } + + @Override + public Void visit(Plus symbol) { + for (Symbol sym : symbol.getSeparators()) + sym.accept(this); + return symbol.getSymbol().accept(this); + } + + @Override + public Void visit(Group symbol) { + for (Symbol sym : symbol.getSymbols()) + sym.accept(this); + return null; + } + + @Override + public Void visit(Star symbol) { + for (Symbol sym : symbol.getSeparators()) + sym.accept(this); + return symbol.getSymbol().accept(this); + } + + @Override + public Void visit(Start start) { +// start.getNonterminal().accept(this); + return null; + } + + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/grammar/transformation/DesugarPrecedenceAndAssociativity.java b/benchmarks/src/main/kotlin/org/iguana/grammar/transformation/DesugarPrecedenceAndAssociativity.java new file mode 100644 index 000000000..b14e8e5a7 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/grammar/transformation/DesugarPrecedenceAndAssociativity.java @@ -0,0 +1,3188 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.grammar.transformation; + +import org.iguana.datadependent.ast.AST; +import org.iguana.datadependent.ast.Expression; +import org.iguana.datadependent.ast.Statement; +import org.iguana.grammar.condition.Condition; +import org.iguana.grammar.condition.DataDependentCondition; +import org.iguana.grammar.runtime.AssociativityGroup; +import org.iguana.grammar.runtime.PrecedenceLevel; +import org.iguana.grammar.runtime.RuntimeGrammar; +import org.iguana.grammar.runtime.RuntimeRule; +import org.iguana.grammar.symbol.Align; +import org.iguana.grammar.symbol.Alt; +import org.iguana.grammar.symbol.Associativity; +import org.iguana.grammar.symbol.Block; +import org.iguana.grammar.symbol.Code; +import org.iguana.grammar.symbol.Conditional; +import org.iguana.grammar.symbol.Error; +import org.iguana.grammar.symbol.Group; +import org.iguana.grammar.symbol.IfThen; +import org.iguana.grammar.symbol.IfThenElse; +import org.iguana.grammar.symbol.Ignore; +import org.iguana.grammar.symbol.Nonterminal; +import org.iguana.grammar.symbol.Offside; +import org.iguana.grammar.symbol.Opt; +import org.iguana.grammar.symbol.Plus; +import org.iguana.grammar.symbol.Return; +import org.iguana.grammar.symbol.Star; +import org.iguana.grammar.symbol.Start; +import org.iguana.grammar.symbol.Symbol; +import org.iguana.grammar.symbol.Terminal; +import org.iguana.grammar.symbol.While; +import org.iguana.traversal.ISymbolVisitor; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static org.iguana.datadependent.ast.AST.TRUE; +import static org.iguana.datadependent.ast.AST.and; +import static org.iguana.datadependent.ast.AST.equal; +import static org.iguana.datadependent.ast.AST.get; +import static org.iguana.datadependent.ast.AST.greater; +import static org.iguana.datadependent.ast.AST.greaterEq; +import static org.iguana.datadependent.ast.AST.ifThenElse; +import static org.iguana.datadependent.ast.AST.integer; +import static org.iguana.datadependent.ast.AST.lShiftANDEqZero; +import static org.iguana.datadependent.ast.AST.lessEq; +import static org.iguana.datadependent.ast.AST.min; +import static org.iguana.datadependent.ast.AST.neg; +import static org.iguana.datadependent.ast.AST.not; +import static org.iguana.datadependent.ast.AST.notEqual; +import static org.iguana.datadependent.ast.AST.or; +import static org.iguana.datadependent.ast.AST.pr1; +import static org.iguana.datadependent.ast.AST.pr2; +import static org.iguana.datadependent.ast.AST.tuple; +import static org.iguana.datadependent.ast.AST.undef; +import static org.iguana.datadependent.ast.AST.var; +import static org.iguana.datadependent.ast.AST.varDeclStat; +import static org.iguana.grammar.condition.DataDependentCondition.predicate; + +/** + * @author Anastasia Izmaylova + */ + +public class DesugarPrecedenceAndAssociativity implements GrammarTransformation { + + private Set leftOrRightRecursiveNonterminals; // when operator precedence and associativity applies + + private Map> headsWithLabeledRules; // excepts + + private enum OP { + _1, + _2 + } + + private OP config_op = OP._2; + + private class Configuration { + + private static final String left = "left"; + private static final String right = "right"; + + // Of associativity groups, rules that specify an associativity different from the group + public Map> left_assoc_rules = new HashMap<>(); + public Map> right_assoc_rules = new HashMap<>(); + public Map> non_assoc_rules = new HashMap<>(); + + // Does not include associativity groups inside groups of rules with the same precedence level + public Map> binary_rules = new HashMap<>(); + public Map> prefix_rules = new HashMap<>(); + public Map> postfix_rules = new HashMap<>(); + + public Map> ibinary_rules = new HashMap<>(); + public Map> iprefix_rules = new HashMap<>(); + public Map> ipostfix_rules = new HashMap<>(); + + public Map right_rec_rules = new HashMap<>(); + + public Set leftEnds = new HashSet<>(); // left ends (transitive) + public Set rightEnds = new HashSet<>(); // right ends (transitive) + + public Set pleftEnds = new HashSet<>(); // from the left ends but where precedence applies + public Set prightEnds = new HashSet<>(); // from the right ends but where precedence applies + + public List pends = new ArrayList<>(); + + public Set rightEndsPrefixBelow = new HashSet<>(); + + public Set directLeftEnds = new HashSet<>(); // not transitive + public Set directRightEnds = new HashSet<>(); // not transitive + public Set rightEndsThatCanLeadToPostfix = new HashSet<>(); + + // Encoding: 2 is 2 & 1; 3 is 1 & 2; 4 is 2 & 2 (l-value & p-arg) + public Map groups = new HashMap<>(); + + private int prefixBelow = -1; + private int postfixBelow = -1; + + private int larity = 1; + private int parity = 1; + + public void arity() { + Collection values = groups.values(); + if (values.contains(2) || values.contains(4)) + larity = 2; + if (values.contains(3) || values.contains(4)) + parity = 2; + } + + boolean hasPrefix(int lhs) { + + if (prefix_rules.containsKey(lhs) && !prefix_rules.get(lhs).isEmpty()) + return true; + + if (iprefix_rules.containsKey(lhs) && !iprefix_rules.get(lhs).isEmpty()) + return true; + + return false; + } + + boolean hasPostfix(int lhs) { + + if (postfix_rules.containsKey(lhs) && !postfix_rules.get(lhs).isEmpty()) + return true; + + if (ipostfix_rules.containsKey(lhs) && !ipostfix_rules.get(lhs).isEmpty()) + return true; + + return false; + } + + boolean hasPrefixBelow(int lhs) { + if (prefixBelow == -1) { + prefixBelow = prefix_rules.isEmpty() ? 0 : prefix_rules.keySet().stream().min(Comparator.naturalOrder()) + .get(); + if (!iprefix_rules.isEmpty()) { + int other = iprefix_rules.keySet().stream().min(Comparator.naturalOrder()).get(); + prefixBelow = Integer.min(prefixBelow, other); + } + } + + if (prefixBelow == 0) + return false; + + if (lhs > prefixBelow) + return true; + if (lhs <= prefixBelow) + return false; + + return false; + } + + @SuppressWarnings("unused") + boolean hasPostfixBelow(int lhs) { + if (postfixBelow == -1) { + postfixBelow = postfix_rules.isEmpty() ? 0 : postfix_rules.keySet().stream().min( + Comparator.naturalOrder()).get(); + if (!ipostfix_rules.isEmpty()) { + int other = ipostfix_rules.keySet().stream().min(Comparator.naturalOrder()).get(); + postfixBelow = Integer.max(postfixBelow, other); + } + } + + if (postfixBelow == 0) + return false; + + if (lhs > postfixBelow) + return true; + if (lhs <= postfixBelow) + return false; + + return false; + } + + @SuppressWarnings("unused") + boolean can_be_reached_via(String nt, String via, String from) { + + if (from != left || from != right) + throw new RuntimeException("Unexpected argument!"); + + if (from == left && this.leftEnds.contains(via) && configs.get(via).pleftEnds.contains(nt)) + return true; + + if (from == right && this.rightEnds.contains(via) && configs.get(via).prightEnds.contains(nt)) + return true; + + return false; + } + + } + + private Map configs; + + public void setOP1() { + config_op = OP._1; + } + + public void setOP2() { + config_op = OP._2; + } + + private static boolean canBePrefix(String nt, String leftEnd, Map configs) { + + if (leftEnd.startsWith("$")) + return true; + + Set ends = new HashSet<>(Arrays.asList(leftEnd)); + Set jobs = ends; + boolean changed = true; + while (changed) { + changed = false; + Set delta = new HashSet<>(); + for (String end : jobs) { + int size = ends.size(); + Set lefts = configs.get(end).directLeftEnds; + if (lefts != null) { + for (String left : lefts) { + if (!left.equals(nt)) { + if (left.startsWith("$") || !configs.get(left).leftEnds.contains(nt)) + return true; + else + delta.add(left); + } + } + } + ends.addAll(delta); + jobs = delta; + if (size != ends.size()) + changed = true; + } + } + return false; + } + + private static boolean canBePostfix( + String nt, + String rightEnd, + Map configs, + boolean compute) { + + Configuration config = configs.get(nt); + + boolean can = false; + + if (rightEnd.startsWith("$")) // nothing to compute + return true; + + Set ends = new HashSet<>(Arrays.asList(rightEnd)); + Set jobs = ends; + boolean changed = true; + while (changed) { + changed = false; + Set delta = new HashSet<>(); + for (String end : jobs) { + int size = ends.size(); + Set rights = configs.get(end).directRightEnds; + if (rights != null) { + for (String right : rights) { + if (!right.equals(nt)) { + if (right.startsWith("$") || !configs.get(right).rightEnds.contains(nt)) { + if (!compute) + return true; + else + can = true; + } else + delta.add(right); + } + } + } + ends.addAll(delta); + jobs = delta; + if (size != ends.size()) + changed = true; + } + } + + if (compute && can) + config.rightEndsThatCanLeadToPostfix.addAll(ends); + + return can; + } + + @Override + public RuntimeGrammar transform(RuntimeGrammar grammar) { + + leftOrRightRecursiveNonterminals = new HashSet<>(); + headsWithLabeledRules = new HashMap<>(); + + FindLabelsUsedInExcepts usedLabels = new FindLabelsUsedInExcepts(); + usedLabels.compute(grammar); + + configs = new HashMap<>(); + + for (RuntimeRule rule : grammar.getRules()) { + + Configuration config = configs.get(rule.getHead().getName()); + + if (config == null) { + config = new Configuration(); + configs.put(rule.getHead().getName(), config); + } + + if (!rule.getLeftEnd().isEmpty()) + config.directRightEnds.add(rule.getRightEnd()); + + if (!rule.getRightEnd().isEmpty()) + config.directRightEnds.add(rule.getRightEnd()); + + config.leftEnds = rule.getLeftEnds(); + config.rightEnds = rule.getRightEnds(); + } + + for (RuntimeRule rule : grammar.getRules()) { + + Nonterminal head = rule.getHead(); + + // 1. Excepts + if (rule.getLabel() != null && usedLabels.getLables().containsKey(rule.getHead().getName()) + && usedLabels.getLables().get(rule.getHead().getName()).contains(rule.getLabel())) { + + Map labels = headsWithLabeledRules.get(head.getName()); + Configuration config = configs.get(head.getName()); + + if (rule.isRightRecursive() || rule.isIRightRecursive()) { + if (rule.isIRightRecursive()) { + boolean canBePostfix = canBePostfix(head.getName(), rule.getRightEnd(), configs, false); + if (!canBePostfix) + config.right_rec_rules.put(rule.getLabel(), rule); + } else + config.right_rec_rules.put(rule.getLabel(), rule); + } + + if (labels != null) { + if (!labels.containsKey(rule.getLabel())) + labels.put(rule.getLabel(), labels.size()); + } else { + labels = new HashMap<>(); + labels.put(rule.getLabel(), labels.size()); + headsWithLabeledRules.put(head.getName(), labels); + } + } + + // 2. Precedence + if (rule.getPrecedenceLevel().getRhs() != 1 + || (rule.getPrecedence() == 1 && rule.getAssociativity() != Associativity.UNDEFINED)) { + leftOrRightRecursiveNonterminals.add(head.getName()); + } + // else: all the rules have a precedence -1 (non-recursive), or 1 and + // undefined associativity; therefore, precedence does not apply + + if (rule.getPrecedence() == -1) + continue; + + // Decision of the arity of a p-argument and l-value (only for the alternative scheme) + Configuration config = configs.get(head.getName()); + AssociativityGroup assoc_group = rule.getAssociativityGroup(); + PrecedenceLevel prec_level = rule.getPrecedenceLevel(); + + boolean isBinary = rule.isLeftRecursive() && rule.isRightRecursive(); + boolean isPrefix = !(rule.isLeftRecursive() || rule.isILeftRecursive()) && rule.isRightRecursive(); + boolean isPostfix = rule.isLeftRecursive() && !(rule.isRightRecursive() || rule.isIRightRecursive()); + + boolean canBeBinary = ((rule.isLeftRecursive() || rule.isILeftRecursive()) && rule.isIRightRecursive()) + || (rule.isILeftRecursive() && (rule.isRightRecursive() || rule.isIRightRecursive())); + + boolean canBePrefix = false; + boolean canBePostfix = false; + + if (rule.isLeftRecursive() && rule.isIRightRecursive()) + canBePostfix = canBePostfix(rule.getHead().getName(), rule.getRightEnd(), configs, true); + + if (rule.isILeftRecursive() && rule.isRightRecursive()) + canBePrefix = canBePrefix(rule.getHead().getName(), rule.getLeftEnd(), configs); + + if (rule.isILeftRecursive() && !rule.isRightRecursive() && !rule.isIRightRecursive()) + canBePostfix = true; + + if (!rule.isLeftRecursive() && !rule.isILeftRecursive() && rule.isIRightRecursive()) + canBePrefix = true; + + if (rule.isILeftRecursive() && rule.isIRightRecursive()) { + canBePrefix = canBePrefix(rule.getHead().getName(), rule.getLeftEnd(), configs); + canBePostfix = canBePostfix(rule.getHead().getName(), rule.getRightEnd(), configs, true); + } + + if (isBinary) { + Set rules = config.binary_rules.get(prec_level.getLhs()); + if (rules == null) { + rules = new HashSet<>(); + config.binary_rules.put(prec_level.getLhs(), rules); + } + rules.add(rule.getPrecedence()); + } + + if (isPrefix) { + Set rules = config.prefix_rules.get(prec_level.getLhs()); + if (rules == null) { + rules = new HashSet<>(); + config.prefix_rules.put(prec_level.getLhs(), rules); + } + rules.add(rule.getPrecedence()); + } + + if (isPostfix) { + Set rules = config.postfix_rules.get(prec_level.getLhs()); + if (rules == null) { + rules = new HashSet<>(); + config.postfix_rules.put(prec_level.getLhs(), rules); + } + rules.add(rule.getPrecedence()); + } + + if (canBeBinary) { + Set rules = config.ibinary_rules.get(prec_level.getLhs()); + if (rules == null) { + rules = new HashSet<>(); + config.ibinary_rules.put(prec_level.getLhs(), rules); + } + rules.add(rule.getPrecedence()); + } + + if (canBePrefix) { + Set rules = config.iprefix_rules.get(prec_level.getLhs()); + if (rules == null) { + rules = new HashSet<>(); + config.prefix_rules.put(prec_level.getLhs(), rules); + } + rules.add(rule.getPrecedence()); + } + + if (canBePostfix) { + Set rules = config.ipostfix_rules.get(prec_level.getLhs()); + if (rules == null) { + rules = new HashSet<>(); + config.postfix_rules.put(prec_level.getLhs(), rules); + } + rules.add(rule.getPrecedence()); + } + + if (assoc_group != null) { + // Inside an associativity group + + Associativity assoc = rule.getAssociativity(); + + if (assoc != assoc_group.getAssociativity() || assoc != Associativity.UNDEFINED) { + // Rules in an associativity group that override the associativity + Set rules = null; + + switch (assoc) { + case LEFT: + rules = config.left_assoc_rules.get(assoc_group.getLhs()); + if (rules == null) { + rules = new HashSet<>(); + config.left_assoc_rules.put(assoc_group.getLhs(), rules); + } + rules.add(rule.getPrecedence()); + break; + case RIGHT: + rules = config.right_assoc_rules.get(assoc_group.getLhs()); + if (rules == null) { + rules = new HashSet<>(); + config.right_assoc_rules.put(assoc_group.getLhs(), rules); + } + rules.add(rule.getPrecedence()); + break; + case NON_ASSOC: + rules = config.non_assoc_rules.get(assoc_group.getLhs()); + if (rules == null) { + rules = new HashSet<>(); + config.non_assoc_rules.put(assoc_group.getLhs(), rules); + } + rules.add(rule.getPrecedence()); + break; + default: + } + } + + boolean climbing = + prec_level.getLhs() == assoc_group.getLhs() && prec_level.getRhs() == assoc_group.getRhs(); + + if (climbing) { + if (isBinary && rule.getPrecedence() != assoc_group.getPrecedence()) + config.groups.put(assoc_group.getLhs(), 4); + if (canBeBinary && rule.getPrecedence() != assoc_group.getPrecedence()) + config.groups.put(assoc_group.getLhs(), 4); + } else + config.groups.put(prec_level.getLhs(), 4); + } + + if (assoc_group == null) { + if (prec_level.getLhs() != prec_level.getRhs() && (canBeBinary || canBePrefix || canBePostfix) + && rule.getAssociativity() != Associativity.UNDEFINED) + config.groups.put(prec_level.getLhs(), 4); + } + + } + + if (config_op == OP._2) { + Set nonterminals = new HashSet<>(); + nonterminals.addAll(leftOrRightRecursiveNonterminals); + + boolean changed = true; + while (changed) { + changed = false; + int size = nonterminals.size(); + Set delta = new HashSet<>(); + for (String nt : nonterminals) { + Configuration config = configs.get(nt); + if (config != null) { + delta.addAll(config.leftEnds); + delta.addAll(config.rightEnds); + } + } + nonterminals.addAll(delta); + if (nonterminals.size() != size) + changed = true; + } + + for (String head : configs.keySet()) { + if (!nonterminals.contains(head)) { + Configuration config = configs.get(head); + config.leftEnds = new HashSet<>(); + config.rightEnds = new HashSet<>(); + } + } + } + + for (RuntimeRule rule : grammar.getRules()) { + + if (config_op == OP._1) break; + + if (rule.getPrecedence() == -1) + continue; + + Configuration config = configs.get(rule.getHead().getName()); + AssociativityGroup assoc_group = rule.getAssociativityGroup(); + PrecedenceLevel prec_level = rule.getPrecedenceLevel(); + + Integer arity = config.groups.get(prec_level.getLhs()); + + if (assoc_group != null) { + + boolean climbing = + prec_level.getLhs() == assoc_group.getLhs() && prec_level.getRhs() == assoc_group.getRhs(); + + if (climbing) { + + if (arity == null || arity != 4) { + + boolean hasBinary = config.binary_rules.get(prec_level.getLhs()) != null; + boolean hasPrefix = config.prefix_rules.get(prec_level.getLhs()) != null; + boolean hasPostfix = config.postfix_rules.get(prec_level.getLhs()) != null; + + boolean canHaveBinary = config.ibinary_rules.get(prec_level.getLhs()) != null; + boolean canHavePrefix = config.iprefix_rules.get(prec_level.getLhs()) != null; + boolean canHavePostfix = config.ipostfix_rules.get(prec_level.getLhs()) != null; + + if (((hasBinary || canHaveBinary) + && (hasPrefix || hasPostfix || canHavePrefix || canHavePostfix)) + || ((hasPrefix || canHavePrefix) && (hasPostfix || canHavePostfix))) { + Associativity assoc = assoc_group.getAssociativity(); + + if ((assoc == Associativity.LEFT || assoc == Associativity.NON_ASSOC) + && (hasPostfix || canHavePostfix)) { + for (int group : config.binary_rules.keySet()) { + if (group > rule.getPrecedence()) { + if (arity == null || arity == 3) + config.groups.put(prec_level.getLhs(), 3); + else + config.groups.put(prec_level.getLhs(), 4); + } + } + for (int group : config.postfix_rules.keySet()) { + if (group > rule.getPrecedence()) { + if (arity == null || arity == 3) + config.groups.put(prec_level.getLhs(), 3); + else + config.groups.put(prec_level.getLhs(), 4); + } + } + + for (int group : config.ibinary_rules.keySet()) { + if (group > rule.getPrecedence()) { + if (arity == null || arity == 3) + config.groups.put(prec_level.getLhs(), 3); + else + config.groups.put(prec_level.getLhs(), 4); + } + } + for (int group : config.ipostfix_rules.keySet()) { + if (group > rule.getPrecedence()) { + if (arity == null || arity == 3) + config.groups.put(prec_level.getLhs(), 3); + else + config.groups.put(prec_level.getLhs(), 4); + } + } + } + if ((assoc == Associativity.RIGHT || assoc == Associativity.NON_ASSOC) + && (hasPrefix || canHavePostfix)) { + for (int group : config.binary_rules.keySet()) { + if (group > rule.getPrecedence()) { + if (arity == null || arity == 2) + config.groups.put(prec_level.getLhs(), 2); + else + config.groups.put(prec_level.getLhs(), 4); + } + } + for (int group : config.postfix_rules.keySet()) { + if (group > rule.getPrecedence()) { + if (arity == null || arity == 2) + config.groups.put(prec_level.getLhs(), 2); + else + config.groups.put(prec_level.getLhs(), 4); + } + } + + for (int group : config.ibinary_rules.keySet()) { + if (group > rule.getPrecedence()) { + if (arity == null || arity == 2) + config.groups.put(prec_level.getLhs(), 2); + else + config.groups.put(prec_level.getLhs(), 4); + } + } + for (int group : config.ipostfix_rules.keySet()) { + if (group > rule.getPrecedence()) { + if (arity == null || arity == 2) + config.groups.put(prec_level.getLhs(), 2); + else + config.groups.put(prec_level.getLhs(), 4); + } + } + } + } + } + } + } else { + + if (arity == null || arity != 4) { + + boolean isBinary = rule.isLeftRecursive() && rule.isRightRecursive(); + boolean canBeBinary = + ((rule.isLeftRecursive() || rule.isILeftRecursive()) && rule.isIRightRecursive()) + || (rule.isILeftRecursive() && (rule.isRightRecursive() || rule.isIRightRecursive())); + + Set binary_rules = config.binary_rules.get(prec_level.getLhs()); + Set prefix_rules = config.prefix_rules.get(prec_level.getLhs()); + Set postfix_rules = config.postfix_rules.get(prec_level.getLhs()); + + Set ibinary_rules = config.ibinary_rules.get(prec_level.getLhs()); + Set iprefix_rules = config.iprefix_rules.get(prec_level.getLhs()); + Set ipostfix_rules = config.ipostfix_rules.get(prec_level.getLhs()); + + int left_rec = (binary_rules == null ? 0 : binary_rules.size()) + + (postfix_rules == null ? 0 : postfix_rules.size()); + + int right_rec = (binary_rules == null ? 0 : binary_rules.size()) + + (prefix_rules == null ? 0 : prefix_rules.size()); + + int ileft_rec = (ibinary_rules == null ? 0 : ibinary_rules.size()) + + (ipostfix_rules == null ? 0 : ipostfix_rules.size()); + + int iright_rec = (ibinary_rules == null ? 0 : ibinary_rules.size()) + + (iprefix_rules == null ? 0 : iprefix_rules.size()); + + if (rule.getAssociativity() != Associativity.UNDEFINED) { + + if (isBinary && (rule.getAssociativity() == Associativity.LEFT + || rule.getAssociativity() == Associativity.NON_ASSOC)) { + // As associatvity is defined, rule.getPrecedence() is unique; + // therefore, maintaining <_>_rules as precedence sets, for each precedence level + // should sufficient + if (left_rec + ileft_rec >= 2) { + Integer n = config.groups.get(prec_level.getLhs()); + if (n == null) + config.groups.put(prec_level.getLhs(), 3); + else if (n != 3) + config.groups.put(prec_level.getLhs(), 4); + } + } + + if (isBinary && (rule.getAssociativity() == Associativity.RIGHT + || rule.getAssociativity() == Associativity.NON_ASSOC)) { + if (right_rec + iright_rec >= 2) { + Integer n = config.groups.get(prec_level.getLhs()); + if (n == null) + config.groups.put(prec_level.getLhs(), 2); + else if (n != 2) + config.groups.put(prec_level.getLhs(), 4); + } + + } + + if (!(isBinary || canBeBinary) && rule.isRightRecursive() + && rule.getAssociativity() == Associativity.NON_ASSOC) { + if (left_rec + ileft_rec >= 1) { + Integer n = config.groups.get(prec_level.getLhs()); + if (n == null) + config.groups.put(prec_level.getLhs(), 3); + else if (n != 3) + config.groups.put(prec_level.getLhs(), 4); + } + } + } + } + } + } + + if (config_op == OP._2) { + for (String head : configs.keySet()) { + + Configuration config = configs.get(head); + + config.arity(); + + for (String end : config.leftEnds) { + if (leftOrRightRecursiveNonterminals.contains(end)) { + config.pleftEnds.add(end); + config.pends.add(end); + } + } + for (String end : config.rightEnds) { + if (leftOrRightRecursiveNonterminals.contains(end)) { + config.prightEnds.add(end); + if (!config.pends.contains(end)) + config.pends.add(end); + } + } + } + + for (RuntimeRule rule : grammar.getRules()) { + + if (rule.getPrecedence() == -1 || !rule.isIRightRecursive()) + continue; + + Configuration config = configs.get(rule.getHead().getName()); + boolean has = config.hasPrefixBelow(rule.getPrecedenceLevel().getLhs()); + if (has && !rule.getRightEnd().startsWith("$")) { + config.rightEndsPrefixBelow.add(rule.getRightEnd()); + boolean changed = true; + while (changed) { + changed = false; + int size = config.rightEndsPrefixBelow.size(); + + Set delta = new HashSet<>(); + for (String end : config.rightEndsPrefixBelow) { + if (configs.get(end) != null) { + for (String right : configs.get(end).directRightEnds) { + if (configs.get(right) != null + && configs.get(right).prightEnds.contains(rule.getHead().getName())) + delta.add(right); + } + } + } + + config.rightEndsPrefixBelow.addAll(delta); + if (size != config.rightEndsPrefixBelow.size()) + changed = true; + } + } + } + } + + Set rules = new LinkedHashSet<>(); + for (RuntimeRule rule : grammar.getRules()) + rules.add(transform(rule)); + + return RuntimeGrammar.builder().addRules(rules).setLayout(grammar.getLayout()) + .setStartSymbols(grammar.getStartSymbols()) + .setEbnfLefts(grammar.getEBNFLefts()) + .setEbnfRights(grammar.getEBNFRights()) + .setGlobals(grammar.getGlobals()) + .setRegularExpressionDefinitions(grammar.getRegularExpressionDefinitions()) + .build(); + } + + public RuntimeRule transform(RuntimeRule rule) { + return new Visitor(rule, leftOrRightRecursiveNonterminals, headsWithLabeledRules, configs, + config_op).transform(); + } + + private static class Visitor implements ISymbolVisitor { + + private final RuntimeRule rule; + + private final Set leftOrRightRecursiveNonterminals; + + private final Map> headsWithLabeledRules; + + private Expression l1; + private Expression r2; + + private Expression l2; + private Expression r1; + + private Set preconditions; + + private boolean isFirst; + private boolean isLast; + + private final OP config_op; + + /* + * Variables of the alternative scheme + */ + + // Priority and associativity related: + private final Map configs; + private final Configuration config; + private int larity = 1; + private int parity = 1; + + private Expression larg = null; + private Expression rarg = null; + + private Expression lcond = null; + private Expression rcond = null; + private Expression ret = null; + + // ! related: + private Expression xlcond = null; + private Expression xrcond = null; + private Expression xret = null; + + private Expression[] lret; + private Expression[] rret; + + Visitor( + RuntimeRule rule, + Set leftOrRightRecursiveNonterminals, + Map> headsWithLabeledRules, + Map configs, + OP config_op) { + this.rule = rule; + this.leftOrRightRecursiveNonterminals = leftOrRightRecursiveNonterminals; + this.headsWithLabeledRules = headsWithLabeledRules; + this.configs = configs; + this.config = configs.get(rule.getHead().getName()); + this.larity = config.larity; + this.parity = config.parity; + + int n = config.pends.size(); + lret = new Expression[n]; + rret = new Expression[n]; + + for (int i = 0; i < n; i++) { + + String end = config.pends.get(i); + + Configuration config = configs.get(end); + + boolean canBeFromLeft = config.leftEnds.contains(rule.getHead().getName()); + boolean canBeFromRight = config.rightEnds.contains(rule.getHead().getName()); + boolean hasPrefixBelow = config.rightEndsPrefixBelow.contains(rule.getHead().getName()); + boolean canBecomePostfix = config.rightEndsThatCanLeadToPostfix.contains(rule.getHead().getName()); + + if (canBeFromLeft && canBeFromRight) { + lret[i] = undef(); + if (hasPrefixBelow || canBecomePostfix) + rret[i] = undef(); + else + rret[i] = null; + } else if (canBeFromLeft) { + lret[i] = undef(); + rret[i] = null; + } else if (canBeFromRight) { + lret[i] = null; + if (hasPrefixBelow || canBecomePostfix) + rret[i] = undef(); + else + rret[i] = null; + } + } + + this.config_op = config_op; + switch (config_op) { + case _1: + excepts1(); + precedence1(); + break; + case _2: + excepts2(); + precedence2(); + break; + } + } + + private void excepts1() { + if (rule.getLabel() != null) { + Map labels = headsWithLabeledRules.get(rule.getHead().getName()); + if (labels != null && labels.containsKey(rule.getLabel())) { + preconditions = new LinkedHashSet<>(); + int l = labels.get(rule.getLabel()); + preconditions.add(predicate(lShiftANDEqZero(var("_not"), integer(l)))); + } + } + } + + private void excepts2() { + Map labels = headsWithLabeledRules.get(rule.getHead().getName()); + + if (labels == null) return; + + if (rule.getLabel() != null && labels.containsKey(rule.getLabel())) { + int l = labels.get(rule.getLabel()); + xrcond = lShiftANDEqZero(var("_not"), integer(l)); + xret = integer(l); + } else + xret = integer(-1); + + if (rule.isLeftRecursive()) { + Nonterminal nonterminal = (Nonterminal) rule.getBody().get(0); + if (nonterminal.getExcepts() != null) { + int n = 0; + for (String except : nonterminal.getExcepts()) { + Integer i = labels.get(except); + + if (i == null) + throw new RuntimeException("Undeclared label: " + except); + + n += 1 << i; + } + + if (!leftOrRightRecursiveNonterminals.contains(rule.getHead().getName())) + xlcond = or(equal(var("l"), integer(-1)), lShiftANDEqZero(integer(n), var("l"))); + else xlcond = or(equal(get(var("l"), 1), integer(-1)), + lShiftANDEqZero(integer(n), get(var("l"), 1))); + } + } + } + + private void precedence1() { // Priority and associativity + if (rule.getPrecedence() == -1) + return; // Precedence does not apply + + if (!leftOrRightRecursiveNonterminals.contains(rule.getHead().getName())) + return; // Precedence does not apply + + if (preconditions == null) + preconditions = new HashSet<>(); + + AssociativityGroup associativityGroup = rule.getAssociativityGroup(); + PrecedenceLevel precedenceLevel = rule.getPrecedenceLevel(); + + int precedence = rule.getPrecedence(); + Associativity associativity = rule.getAssociativity(); + + boolean nUseMin = false; + + // 1. Expressions for the left and/or right recursive uses + + if (associativityGroup != null + && precedenceLevel.getLhs() == associativityGroup.getLhs() + && precedenceLevel.getRhs() == associativityGroup.getRhs()) { + + if (precedence == associativityGroup.getPrecedence()) { // Can use precedence climbing + boolean first = precedenceLevel.getUndefined() == 0; + switch (associativityGroup.getAssociativity()) { + case LEFT: + l1 = integer(first ? 0 : precedence); + r2 = integer(precedenceLevel.getRhs() + 1); + break; + case RIGHT: + l1 = integer(precedenceLevel.getRhs() + 1); + r2 = integer(first ? 0 : precedence); + break; + case NON_ASSOC: + l1 = integer(precedenceLevel.getRhs() + 1); + r2 = integer(precedenceLevel.getRhs() + 1); + break; + default: + throw new RuntimeException( + "Unexpected associativity: " + associativityGroup.getAssociativity()); + } + + // Rule for propagation of a precedence level + if (precedenceLevel.hasPostfixUnaryBelow()) + r1 = nUseMin ? var("r") : pr(precedence, precedenceLevel.postfixUnaryBelow, false); + else if (precedenceLevel.hasPostfixUnary()) + r1 = integer(first ? 0 : precedence); + else + r1 = l1; + + if (precedenceLevel.hasPrefixUnaryBelow()) + l2 = nUseMin ? var("l") : pr(precedence, precedenceLevel.prefixUnaryBelow, true); + else if (precedenceLevel.hasPrefixUnary()) + l2 = integer(first ? 0 : precedence); + else + l2 = r2; + } else { + l1 = integer(precedence); + r2 = integer(precedence); + + // Rule for propagation of a precedence level + l2 = nUseMin ? var("l") : precedenceLevel.hasPrefixUnaryBelow() + ? pr(precedence, precedenceLevel.prefixUnaryBelow, true) : integer(0); + r1 = nUseMin ? var("r") : precedenceLevel.hasPostfixUnaryBelow() + ? pr(precedence, precedenceLevel.postfixUnaryBelow, false) : integer(0); + } + // Can use precedence climbing + } else if (associativityGroup == null && precedenceLevel.getLhs() == precedenceLevel.getRhs()) { + boolean first = precedenceLevel.getUndefined() == 0; + int il1 = -1; + int ir2 = -1; + switch (associativity) { + case LEFT: + il1 = first ? 0 : precedence; + l1 = integer(il1); + ir2 = precedence + 1; + r2 = integer(ir2); + break; + case RIGHT: + il1 = precedence + 1; + l1 = integer(il1); + ir2 = first ? 0 : precedence; + r2 = integer(ir2); + break; + case NON_ASSOC: + il1 = precedence + 1; + l1 = integer(il1); + ir2 = precedence + 1; + r2 = integer(ir2); + break; + case UNDEFINED: + il1 = first ? 0 : precedence; + l1 = integer(il1); + ir2 = first ? 0 : precedence; + r2 = integer(ir2); + break; + default: + throw new RuntimeException("Unexpected associativity: " + associativity); + } + + // Rule for propagation of a precedence level + if (precedenceLevel.hasPostfixUnaryBelow()) + r1 = nUseMin ? var("r") : pr(precedenceLevel.hasPostfixUnary() ? precedence : il1, + precedenceLevel.postfixUnaryBelow, false); + else if (precedenceLevel.hasPostfixUnary()) + r1 = integer(first ? 0 : precedence); + else + r1 = l1; + + if (precedenceLevel.hasPrefixUnaryBelow()) + l2 = nUseMin ? var("l") : pr(precedenceLevel.hasPrefixUnary() ? precedence : ir2, + precedenceLevel.prefixUnaryBelow, true); + else if (precedenceLevel.hasPrefixUnary()) + l2 = integer(first ? 0 : precedence); + else + l2 = r2; + } else { // No precedence climbing + int undefined = precedenceLevel.getUndefined(); + boolean useUndefined = (associativityGroup == null + || (associativityGroup.getPrecedence() == precedence)) + && undefined != -1; + + switch ((associativityGroup != null && associativity == Associativity.UNDEFINED) + ? associativityGroup.getAssociativity() : associativity) { + case LEFT: + l1 = integer(useUndefined ? undefined : precedence); + r2 = integer(precedence); + // Rule for propagation of a precedence level + l2 = nUseMin ? var("l") : precedenceLevel.hasPrefixUnaryBelow() + ? pr(precedence, precedenceLevel.prefixUnaryBelow, true) : integer(0); + r1 = nUseMin ? var("r") : precedenceLevel.hasPostfixUnaryBelow() + ? pr(precedence, precedenceLevel.postfixUnaryBelow, false) + : integer(useUndefined ? undefined : 0); + break; + case RIGHT: + l1 = integer(precedence); + r2 = integer(useUndefined ? undefined : precedence); + // Rule for propagation of a precedence level + l2 = nUseMin ? var("l") : precedenceLevel.hasPrefixUnaryBelow() + ? pr(precedence, precedenceLevel.prefixUnaryBelow, true) + : integer(useUndefined ? undefined : 0); + r1 = nUseMin ? var("r") : precedenceLevel.hasPostfixUnaryBelow() + ? pr(precedence, precedenceLevel.postfixUnaryBelow, false) + : integer(0); + break; + case NON_ASSOC: + l1 = integer(precedence); + r2 = integer(precedence); + // Rule for propagation of a precedence level + l2 = nUseMin ? var("l") : precedenceLevel.hasPrefixUnaryBelow() + ? pr(precedence, precedenceLevel.prefixUnaryBelow, true) + : integer(0); + r1 = nUseMin ? var("r") : precedenceLevel.hasPostfixUnaryBelow() + ? pr(precedence, precedenceLevel.postfixUnaryBelow, false) + : integer(0); + break; + case UNDEFINED: // Not in the associativity group + l1 = integer(undefined); + r2 = integer(undefined); + // Rule for propagation of a precedence level + l2 = nUseMin ? var("l") : precedenceLevel.hasPrefixUnaryBelow() + ? pr(precedence, precedenceLevel.prefixUnaryBelow, true) + : integer(undefined); + r1 = nUseMin ? var("r") : precedenceLevel.hasPostfixUnaryBelow() + ? pr(precedence, precedenceLevel.postfixUnaryBelow, false) + : integer(undefined); + break; + default: + throw new RuntimeException("Unexpected associativity: " + associativity); + } + } + + // 2. Constraints (preconditions) for the grammar rule + + if (rule.isLeftRecursive()) + preconditions.add(predicate(greaterEq(integer(precedenceLevel.getRhs()), var("r")))); + + if (rule.isRightRecursive()) + preconditions.add(predicate(greaterEq(integer(precedenceLevel.getRhs()), var("l")))); + + if (precedenceLevel.getLhs() != precedenceLevel.getRhs()) { + + if (associativityGroup != null) { + + boolean climbing = associativityGroup.getLhs() == precedenceLevel.getLhs() + && associativityGroup.getRhs() == precedenceLevel.getRhs(); + + switch (associativityGroup.getAssociativity()) { + case LEFT: + if (rule.isLeftRecursive()) { + if (!climbing) + preconditions.add( + predicate(notEqual(integer(associativityGroup.getPrecedence()), var("r")))); + + if (!associativityGroup.getAssocMap().isEmpty()) + for (Map.Entry entry : associativityGroup.getAssocMap() + .entrySet()) + if (precedence != entry.getKey()) + preconditions.add(predicate(notEqual(integer(entry.getKey()), var("r")))); + } + break; + case RIGHT: + if (rule.isRightRecursive()) { + if (!climbing) + preconditions.add( + predicate(notEqual(integer(associativityGroup.getPrecedence()), var("l")))); + + if (!associativityGroup.getAssocMap().isEmpty()) + for (Map.Entry entry : associativityGroup.getAssocMap() + .entrySet()) + if (precedence != entry.getKey() + && !(climbing && entry.getKey() == associativityGroup.getPrecedence())) + preconditions.add(predicate(notEqual(integer(entry.getKey()), var("l")))); + } + break; + case NON_ASSOC: + if (!climbing) + preconditions.add( + predicate(notEqual(integer(associativityGroup.getPrecedence()), var("r")))); + if (!climbing) + preconditions.add( + predicate(notEqual(integer(associativityGroup.getPrecedence()), var("l")))); + + if (!associativityGroup.getAssocMap().isEmpty()) { + for (Map.Entry entry : associativityGroup.getAssocMap() + .entrySet()) { + if (precedence != entry.getKey() + && !(climbing && entry.getKey() == associativityGroup.getPrecedence())) { + preconditions.add(predicate(notEqual(integer(entry.getKey()), var("r")))); + preconditions.add(predicate(notEqual(integer(entry.getKey()), var("l")))); + } + } + } + break; + default: + throw new RuntimeException( + "Unexpected associativity: " + associativityGroup.getAssociativity()); + } + + if (precedence != associativityGroup.getPrecedence()) { + switch (associativity) { + case LEFT: + if (rule.isLeftRecursive()) + preconditions.add(predicate(notEqual(integer(precedence), var("r")))); + break; + case RIGHT: + if (rule.isRightRecursive()) + preconditions.add(predicate(notEqual(integer(precedence), var("l")))); + break; + case NON_ASSOC: + preconditions.add(predicate(notEqual(integer(precedence), var("l")))); + preconditions.add(predicate(notEqual(integer(precedence), var("r")))); + break; + case UNDEFINED: + break; + default: + throw new RuntimeException("Unexpected associativity: " + associativity); + } + } + } else { + switch (associativity) { + case LEFT: + if (rule.isLeftRecursive()) + preconditions.add(predicate(notEqual(integer(precedence), var("r")))); + break; + case RIGHT: + if (rule.isRightRecursive()) + preconditions.add(predicate(notEqual(integer(precedence), var("l")))); + break; + case NON_ASSOC: + preconditions.add(predicate(notEqual(integer(precedence), var("l")))); + preconditions.add(predicate(notEqual(integer(precedence), var("r")))); + break; + case UNDEFINED: + break; + default: + throw new RuntimeException("Unexpected associativity: " + associativity); + } + } + } + } + + private void precedence2() { // Priority and associativity + + if (!leftOrRightRecursiveNonterminals.contains(rule.getHead().getName())) + return; // Precedence does not apply + + if (rule.getPrecedence() == -1) { + if (this.larity == 1) + ret = integer(0); + else + ret = tuple(integer(0), integer(0)); + return; // Precedence does not apply + } + + PrecedenceLevel prec_level = rule.getPrecedenceLevel(); + AssociativityGroup assoc_group = rule.getAssociativityGroup(); + + int prec = rule.getPrecedence(); + Associativity assoc = rule.getAssociativity(); + + int undefined = prec_level.getUndefined(); + boolean first = undefined == 0; + + boolean labeled = headsWithLabeledRules.containsKey(rule.getHead().getName()); + + // Either prec or (prec,assoc) + Expression lprec = null; + Expression lassoc = null; + Expression rprec = null; + Expression pprec = null; + Expression passoc = null; + + if (labeled && larity == 2) { + lprec = get(get(var("l"), 0), 0); // l.0.0 + lassoc = get(get(var("l"), 0), 1); // l.0.1 + rprec = get(get(var("r"), 0), 0); // r.0.0 + } else if (labeled && larity != 2) { + lprec = get(var("l"), 0); // l.0 + lassoc = get(var("l"), 0); // l.0 + rprec = get(var("r"), 0); // l.0 + } else if (!labeled && larity == 2) { + lprec = get(var("l"), 0); // l.0 + lassoc = get(var("l"), 1); // l.1 + rprec = get(var("r"), 0); // l.0 + } else { + lprec = var("l"); + lassoc = var("l"); + rprec = var("r"); + } + + if (parity == 2) { + pprec = get(var("p"), 0); // p.0 + passoc = get(var("p"), 1); // p.1 + } else { + pprec = var("p"); // p + passoc = var("p"); // p + } + + if (parity == 2) + larg = tuple(pprec, integer(0)); + else + larg = pprec; + + boolean canBePrefix = false; + boolean canBePostfix = false; + + if (rule.isLeftRecursive() && rule.isIRightRecursive()) + canBePostfix = canBePostfix(rule.getHead().getName(), rule.getRightEnd(), configs, false); + + if (rule.isILeftRecursive() && rule.isRightRecursive()) + canBePrefix = canBePrefix(rule.getHead().getName(), rule.getLeftEnd(), configs); + + if (rule.isILeftRecursive() && !rule.isRightRecursive() && !rule.isIRightRecursive()) + canBePostfix = true; + + if (!rule.isLeftRecursive() && !rule.isILeftRecursive() && rule.isIRightRecursive()) + canBePrefix = true; + + if (rule.isILeftRecursive() && rule.isIRightRecursive()) { + canBePrefix = canBePrefix(rule.getHead().getName(), rule.getLeftEnd(), configs); + canBePostfix = canBePostfix(rule.getHead().getName(), rule.getRightEnd(), configs, false); + } + + // 1. Expressions for the left and/or right recursive uses + if (assoc_group != null) { + + if ((rule.isILeftRecursive() + || rule.isIRightRecursive() && assoc_group.getAssociativity() != Associativity.NON_ASSOC)) + throw new RuntimeException( + "Not yet implemented: indirect recursion inside a left or right associativity group"); + + // Local to an associativity group + int arity = config.groups.get(prec_level.getLhs()) == null ? 1 : config.groups.get(prec_level.getLhs()); + int larity = (arity == 2 || arity == 4) ? 2 : 1; + int parity = (arity == 3 || arity == 4) ? 2 : 1; + + if (assoc_group.getLhs() == prec_level.getLhs() && assoc_group.getRhs() == prec_level.getRhs()) + undefined = assoc_group.getPrecedence(); + + // ***Climbing condition now: larity == 1 || parity == 1 + + Expression negative = lessEq(lprec, integer(0)); + + switch (assoc_group.getAssociativity()) { + + case LEFT: // restricts right end + + if (rule.isLeftRecursive()) + lcond = or(negative, greaterEq(lprec, integer(prec_level.getLhs()))); + + if (rule.isLeftRecursive()) { + + rcond = greaterEq(integer(prec_level.getRhs()), pprec); + + if (parity == 1) { + Set prefix_rules = config.prefix_rules.get(prec_level.getLhs()); + Set non_assoc_rules = config.non_assoc_rules.get(prec_level.getLhs()); + + if (prefix_rules != null && non_assoc_rules != null) { + for (int p : prefix_rules) + if (non_assoc_rules.contains(p)) + rcond = and(rcond, not(equal(pprec, integer(p)))); + } + } + + if (parity == 2) { + if (rule.getAssociativity() == Associativity.RIGHT) + rcond = and(rcond, or(equal(passoc, integer(prec)), + not(and(greaterEq(integer(assoc_group.getRhs()), passoc), + greaterEq(passoc, integer(assoc_group.getLhs())))))); + else + rcond = and(rcond, not(and(greaterEq(integer(assoc_group.getRhs()), passoc), + greaterEq(passoc, integer(assoc_group.getLhs()))))); + } + } + + if (parity == 2) { + rarg = tuple(integer(prec_level.getRhs()), integer(prec)); // as constrained right end + } else { + if (this.parity == 2) + rarg = tuple(integer(prec_level.getRhs() + 1), integer(0)); + else + rarg = integer(prec_level.getRhs() + 1); + } + + if (rule.isRightRecursive()) { + if (prec_level.hasPrefixUnaryBelow()) { + if (larity == 2) + // as unconstrained left end + ret = tuple(minimum(prec_level.getLhs(), rprec), integer(0)); + else { + if (this.larity == 2) + // as unconstrained left end + ret = tuple(minimum(undefined != -1 ? undefined : prec, rprec), integer(0)); + else + // as unconstrained left end + ret = minimum(undefined != -1 ? undefined : prec, rprec); + } + } else { + if (larity == 2) + ret = tuple(integer(prec_level.getLhs()), integer(0)); + else { + if (this.larity == 2) + ret = tuple(integer(undefined != -1 ? undefined : prec), integer(0)); + else + ret = integer(undefined != -1 ? undefined : prec); + } + } + } else { + if (this.larity == 2) + ret = tuple(integer(0), integer(0)); + else + ret = integer(0); + } + + if (prec != assoc_group.getPrecedence()) { + + switch (rule.getAssociativity()) { + + case NON_ASSOC: + if (rule.isLeftRecursive() && rule.isRightRecursive()) { + + if (larity == 2) + lcond = and(or(negative, greaterEq(lprec, integer(prec_level.getLhs()))), + notEqual(lassoc, integer(prec))); + else + lcond = or(negative, and(notEqual(lprec, integer(prec)), + greaterEq(lprec, integer(prec_level.getLhs())))); + if (parity == 2) + rarg = tuple(integer(prec_level.getRhs()), + // as constrained right end + integer(assoc_group.getPrecedence() != -1 + ? assoc_group.getPrecedence() : prec)); + + if (prec_level.hasPrefixUnaryBelow()) { + if (larity == 2) + ret = tuple(minimum(prec_level.getLhs(), rprec), integer(prec)); + else { + if (this.larity == 2) + ret = tuple(minimum(prec, rprec), integer(0)); + else + ret = minimum(prec, rprec); + } + } else { + if (larity == 2) + ret = tuple(integer(prec_level.getLhs()), integer(prec)); + else { + if (this.larity == 2) + ret = tuple(integer(prec), integer(0)); + else + ret = integer(prec); + } + } + + } else if (rule.isLeftRecursive()) { + + if (larity == 2) { + lcond = and(or(negative, greaterEq(lprec, integer(prec_level.getLhs()))), + notEqual(lassoc, neg(integer(prec)))); + ret = tuple(integer(0), neg(integer(prec))); + } else { + lcond = or(and(negative, notEqual(lprec, neg(integer(prec)))), + greaterEq(lprec, integer(prec_level.getLhs()))); + if (this.larity == 2) + ret = tuple(neg(integer(prec)), integer(0)); + else + ret = neg(integer(prec)); + } + + } else { + if (parity == 1) { + if (this.parity == 2) + rarg = tuple(integer(prec), integer(0)); + else + rarg = integer(prec); + } + + rcond = notEqual(parity == 2 ? passoc : pprec, integer(prec)); + } + break; + case RIGHT: // larity == 2 + if (rule.isLeftRecursive() && rule.isRightRecursive()) { + + lcond = and(or(negative, greaterEq(lprec, integer(prec_level.getLhs()))), + notEqual(lassoc, integer(prec))); + + if (prec_level.hasPrefixUnaryBelow()) { + if (larity == 2) + ret = tuple(minimum(prec_level.getLhs(), rprec), integer(prec)); + else { + if (this.larity == 2) + ret = tuple(minimum(prec, rprec), integer(0)); + else + ret = minimum(prec, rprec); + } + } else { + if (larity == 2) + ret = tuple(integer(prec_level.getLhs()), integer(prec)); + else { + if (this.larity == 2) + ret = tuple(integer(prec), integer(0)); + else + ret = integer(prec); + } + } + } + break; + default: + break; + } + } + + break; + + case RIGHT: // restricts left end + + if (rule.isLeftRecursive()) { + + if (larity == 2) { + if (rule.getAssociativity() == Associativity.LEFT) { + lcond = and(or(negative, greaterEq(lprec, integer(prec_level.getLhs()))), + or(lessEq(lassoc, integer(0)), + or(equal(lassoc, integer(prec)), + not(and(greaterEq(integer(assoc_group.getRhs()), lassoc), + greaterEq(lassoc, integer(assoc_group.getLhs()))))))); + } else + lcond = and(or(negative, greaterEq(lprec, integer(prec_level.getLhs()))), + or(lessEq(lassoc, integer(0)), + not(and(greaterEq(integer(assoc_group.getRhs()), lassoc), + greaterEq(lassoc, integer(assoc_group.getLhs())))))); + } else + lcond = or(negative, greaterEq(lprec, integer(prec_level.getRhs() + 1))); + } + + if (rule.isLeftRecursive()) + rcond = greaterEq(integer(prec_level.getRhs()), pprec); + + if (parity == 2) + rarg = tuple(integer(prec_level.getRhs()), integer(0)); // as right end is unconstrained + else { + if (this.parity == 2) + // as right end is unconstrained + rarg = tuple(integer(undefined != -1 ? undefined : prec), integer(0)); + else + rarg = integer(undefined != -1 ? undefined : prec); // as right end is unconstrained + } + + if (rule.isRightRecursive()) { + + if (prec_level.hasPrefixUnaryBelow()) { + if (larity == 2) + // as constrained left end + ret = tuple(minimum(prec_level.getLhs(), rprec), integer(prec)); + else { + if (this.larity == 2) + ret = tuple(minimum(prec, rprec), integer(0)); // as constrained left end + else + ret = minimum(prec, rprec); // as constrained left end + } + } else { + if (larity == 2) + ret = tuple(integer(prec_level.getLhs()), integer(prec)); + else { + if (this.larity == 2) + ret = tuple(integer(prec), integer(0)); + else + ret = integer(prec); + } + } + + } else { + if (parity == 2) + ret = tuple(integer(0), integer(0)); + else + ret = integer(0); + } + + if (prec != assoc_group.getPrecedence()) { + + switch (rule.getAssociativity()) { + + case NON_ASSOC: + if (rule.isLeftRecursive() && rule.isRightRecursive()) { + + if (parity == 2) { + rarg = tuple(integer(prec_level.getRhs()), integer(prec)); + } else { + if (this.parity == 2) + rarg = tuple(integer(prec), integer(0)); + else + rarg = integer(prec); + } + + rcond = and(greaterEq(integer(prec_level.getRhs()), pprec), + not(equal(parity == 2 ? passoc : pprec, integer(prec)))); + + int newprec = + assoc_group.getPrecedence() != -1 ? assoc_group.getPrecedence() : prec; + if (prec_level.hasPrefixUnaryBelow()) { + if (larity == 2) + ret = tuple(minimum(prec_level.getLhs(), rprec), integer(newprec)); + else { + if (this.larity == 2) + ret = tuple(minimum(newprec, rprec), integer(0)); + else + ret = minimum(newprec, rprec); + } + } else { + if (larity == 2) + ret = tuple(integer(prec_level.getLhs()), integer(newprec)); + else { + if (this.larity == 2) + ret = tuple(integer(newprec), integer(0)); + else + ret = integer(newprec); + } + } + + } else if (rule.isLeftRecursive()) { + + if (larity == 2) { + lcond = and(or(negative, greaterEq(lprec, integer(prec_level.getLhs()))), + or(and(lessEq(lassoc, integer(0)), + notEqual(lassoc, neg(integer(prec)))), + and(greater(lassoc, integer(0)), + not(and(greaterEq(integer(assoc_group.getRhs()), lassoc), + greaterEq(lassoc, + integer(assoc_group.getLhs()))))))); + + ret = tuple(integer(0), neg(integer(prec))); + } else { + lcond = or(and(negative, notEqual(lprec, neg(integer(prec)))), + greaterEq(lprec, integer(prec_level.getRhs() + 1))); + if (this.larity == 2) + ret = tuple(neg(integer(prec)), integer(0)); + else + ret = neg(integer(prec)); + } + + } else { + + if (parity == 2) + rarg = tuple(integer(prec_level.getRhs()), integer(prec)); + else { + if (this.parity == 2) + rarg = tuple(integer(prec), integer(0)); + else + rarg = integer(prec); + } + + rcond = not(equal(parity == 2 ? passoc : pprec, integer(prec))); + + int newprec = + assoc_group.getPrecedence() != -1 ? assoc_group.getPrecedence() : prec; + if (prec_level.hasPrefixUnaryBelow()) { + if (larity == 2) + ret = tuple(minimum(prec_level.getLhs(), rprec), integer(newprec)); + else { + if (this.larity == 2) + ret = tuple(minimum(newprec, rprec), integer(0)); + else + ret = minimum(newprec, rprec); + } + } else { + if (larity == 2) + ret = tuple(integer(prec_level.getLhs()), integer(newprec)); + else { + if (this.larity == 2) + ret = tuple(integer(newprec), integer(0)); + else + ret = integer(newprec); + } + } + } + break; + case LEFT: // parity == 2 + if (rule.isLeftRecursive() && rule.isRightRecursive()) { + rarg = tuple(integer(prec_level.getRhs()), integer(prec)); + + rcond = and(greaterEq(integer(prec_level.getRhs()), pprec), + not(equal(passoc, integer(prec)))); + } + break; + default: + break; + } + } + + break; + // restricts both ends (in contrast to other associativity groups applies regardless ends) + case NON_ASSOC: + + if (larity == 1 && parity == 1 && !config.hasPrefix(prec_level.getLhs()) + && !config.hasPostfix(prec_level.getLhs())) { + + lcond = or(negative, greaterEq(lprec, integer(prec_level.getRhs() + 1))); + + rcond = greaterEq(integer(prec_level.getRhs()), pprec); + + if (this.parity == 2) + rarg = tuple(integer(prec_level.getRhs() + 1), integer(0)); + else + rarg = integer(prec_level.getRhs() + 1); + + if (prec_level.hasPrefixUnaryBelow()) { + if (this.larity == 2) + ret = tuple(minimum(prec, rprec), integer(0)); + else + ret = minimum(prec, rprec); + } else { + if (this.larity == 2) + ret = tuple(integer(prec), integer(0)); + else + ret = integer(prec); + } + + } else { + + if (rule.isLeftRecursive() || rule.isILeftRecursive()) { + + if (rule.getAssociativity() == Associativity.LEFT) // larity == 2 + lcond = and(or(and(lessEq(lassoc, integer(0)), + not(and(greaterEq(neg(integer(assoc_group.getLhs())), lassoc), + greaterEq(lassoc, neg(integer(assoc_group.getRhs())))))), + and(greater(lassoc, integer(0)), + or(equal(lassoc, integer(prec)), + not(and(greaterEq(integer(assoc_group.getRhs()), lassoc), + greaterEq(lassoc, integer(assoc_group.getLhs()))))))), + or(negative, greaterEq(lprec, integer(prec_level.getLhs())))); + else + lcond = and(or(and(lessEq(larity == 2 ? lassoc : lprec, integer(0)), + assoc_group.getLhs() == assoc_group.getRhs() + ? notEqual(larity == 2 ? lassoc : lprec, + neg(integer(assoc_group.getLhs()))) + : not(and(greaterEq(neg(integer(assoc_group.getLhs())), + larity == 2 ? lassoc : lprec), + greaterEq(larity == 2 ? lassoc : lprec, + neg(integer( + assoc_group.getRhs())))))), + and(greater(larity == 2 ? lassoc : lprec, integer(0)), + assoc_group.getLhs() == assoc_group.getRhs() + ? notEqual(larity == 2 ? lassoc : lprec, + integer(assoc_group.getLhs())) + : not(and(greaterEq(integer(assoc_group.getRhs()), + larity == 2 ? lassoc : lprec), + greaterEq(larity == 2 ? lassoc : lprec, + integer(assoc_group.getLhs())))))), + or(negative, greaterEq(lprec, integer(prec_level.getLhs())))); + + if (rule.isILeftRecursive() && canBePrefix) + lcond = or(equal(var("l"), undef()), lcond); + } + + if (rule.isLeftRecursive() || rule.isILeftRecursive()) { + if (rule.getAssociativity() == Associativity.RIGHT) // parity == 2 + rcond = and(or(equal(passoc, integer(prec)), + not(and(greaterEq(integer(assoc_group.getRhs()), passoc), + greaterEq(passoc, integer(assoc_group.getLhs()))))), + greaterEq(integer(prec_level.getRhs()), pprec)); + else + rcond = and(assoc_group.getLhs() == assoc_group.getRhs() + ? notEqual(parity == 2 ? passoc : pprec, + integer(assoc_group.getLhs())) + : not(and(greaterEq(integer(assoc_group.getRhs()), + parity == 2 ? passoc : pprec), + greaterEq(parity == 2 ? passoc : pprec, + integer(assoc_group.getLhs())))), + greaterEq(integer(prec_level.getRhs()), pprec)); + } + + if (rule.isILeftRecursive() && canBePrefix) { + rcond = ifThenElse(equal(var("l"), undef()), + assoc_group.getLhs() == assoc_group.getRhs() + ? notEqual(parity == 2 ? passoc : pprec, + integer(assoc_group.getLhs())) + : not(and(greaterEq(integer(assoc_group.getRhs()), + parity == 2 ? passoc : pprec), + greaterEq(parity == 2 ? passoc : pprec, + integer(assoc_group.getLhs())))), + rcond); // TODO: should become post-condition + } + + if (rule.isRightRecursive() || rule.isIRightRecursive()) { + if (parity == 2) + rarg = tuple(integer(prec_level.getRhs()), integer(prec)); + else { + if (this.parity == 2) + rarg = tuple(integer(prec), integer(0)); + else + rarg = integer(prec); + } + } + + if (rule.isRightRecursive() || rule.isIRightRecursive()) { + if (config.hasPrefixBelow(prec_level.getLhs())) { + if (larity == 2) + ret = tuple(minimum(prec_level.getLhs(), rprec), integer(prec)); + else { + if (this.larity == 2) + ret = tuple(minimum(prec, rprec), integer(0)); + else + ret = minimum(prec, rprec); + } + } else { + if (larity == 2) + ret = tuple(integer(prec_level.getLhs()), integer(prec)); + else { + if (this.larity == 2) + ret = tuple(integer(prec), integer(0)); + else + ret = integer(prec); + } + } + + if (rule.isIRightRecursive() && canBePostfix) { + if (rule.isLeftRecursive()) + ret = ifThenElse(equal(var("r"), undef()), + larity == 2 ? tuple(integer(0), neg(integer(prec))) + : (this.larity == 2 ? tuple(neg(integer(prec)), + integer(0)) + : neg(integer(prec))), + ret); + else if (rule.isILeftRecursive()) + ret = ifThenElse(equal(var("r"), undef()), + ifThenElse(equal(var("l"), undef()), + larity == 2 ? tuple(integer(0), integer(0)) + : integer(0), + larity == 2 ? tuple(integer(0), neg(integer(prec))) + : (this.larity == 2 + ? tuple(neg(integer(prec)), integer(0)) + : neg(integer(prec)))), + ret); + else + throw new RuntimeException("Shouldn't have happened!"); + } + + } else { + if (this.larity == 2) + ret = tuple(integer(0), integer(0)); + else + ret = integer(prec); + } + } + + // Normal prefix + if (!(rule.isLeftRecursive() || rule.isILeftRecursive()) && rule.isRightRecursive()) + rcond = assoc_group.getLhs() == assoc_group.getRhs() + ? notEqual(parity == 2 ? passoc : pprec, integer(assoc_group.getLhs())) + : not(and(greaterEq(integer(assoc_group.getRhs()), parity == 2 ? passoc : pprec), + greaterEq(parity == 2 ? passoc : pprec, + integer(assoc_group.getLhs())))); + + // Normal postfix + if (rule.isLeftRecursive() && !(rule.isRightRecursive() || rule.isIRightRecursive())) { + if (larity == 2) + ret = tuple(integer(0), neg(integer(prec))); + else { + if (this.larity == 2) + ret = tuple(neg(integer(prec)), integer(0)); + else + ret = neg(integer(prec)); + } + } + + break; + default: + throw new RuntimeException("Unexpected associativity: " + assoc_group.getAssociativity()); + } + + } + + if (assoc_group == null && prec_level.getLhs() == prec_level.getRhs()) { // larity == 1 && parity == 1 + switch (assoc) { + case LEFT: + + if (rule.isLeftRecursive() || rule.isILeftRecursive()) { + + lcond = or(lessEq(lprec, integer(0)), greaterEq(lprec, integer(prec))); + rcond = greaterEq(integer(prec), pprec); + + if (rule.isILeftRecursive() && canBePrefix) { + lcond = or(equal(var("l"), undef()), lcond); + // TODO: should become post-condition + rcond = ifThenElse(equal(var("l"), undef()), TRUE, rcond); + } + } + + if (rule.isRightRecursive() || rule.isIRightRecursive()) { + if (this.parity == 2) + rarg = tuple(integer(prec + 1), integer(0)); + else + rarg = integer(prec + 1); + + // larity == 1 + if (config.hasPrefixBelow(prec_level.getLhs())) { + if (this.larity == 2) + ret = tuple(minimum(prec, rprec), integer(0)); + else + ret = minimum(prec, rprec); + + if (rule.isIRightRecursive() && canBePostfix) + ret = ifThenElse(equal(var("r"), undef()), + this.larity == 2 ? tuple(integer(0), integer(0)) : integer(0), + ret); + + } else { // In this case, r-variable is not used + if (this.larity == 2) + ret = tuple(integer(prec), integer(0)); + else + ret = integer(prec); + } + } else { + if (this.larity == 2) + ret = tuple(integer(0), integer(0)); + else + ret = integer(0); + } + + break; + case RIGHT: + + if (rule.isLeftRecursive() || rule.isILeftRecursive()) { + lcond = or(lessEq(lprec, integer(0)), greaterEq(lprec, integer(prec + 1))); + rcond = greaterEq(integer(prec), pprec); + + if (rule.isILeftRecursive() && canBePrefix) { + lcond = or(equal(var("l"), undef()), lcond); + // TODO: should become post-condition + rcond = ifThenElse(equal(var("l"), undef()), TRUE, rcond); + } + } + + if (rule.isRightRecursive() || rule.isIRightRecursive()) { + if (this.parity == 2) + rarg = tuple(integer(first ? 0 : prec), integer(0)); + else + rarg = integer(first ? 0 : prec); + + // larity == 1 + if (config.hasPrefixBelow(prec_level.getLhs())) { + if (this.larity == 2) + ret = tuple(minimum(prec, rprec), integer(0)); + else + ret = minimum(prec, rprec); + + if (rule.isIRightRecursive() && canBePostfix) + ret = ifThenElse(equal(var("r"), undef()), + this.larity == 2 ? tuple(integer(0), integer(0)) : integer(0), + ret); + } else { + if (this.larity == 2) + ret = tuple(integer(prec), integer(0)); + else + ret = integer(prec); + } + } else { + if (this.larity == 2) + ret = tuple(integer(0), integer(0)); + else + ret = integer(0); + } + + break; + case NON_ASSOC: // TODO: non all the cases have been yet supported + if ((rule.isLeftRecursive() && rule.isRightRecursive()) + || (rule.isILeftRecursive() && rule.isRightRecursive() && !canBePrefix) + || (rule.isLeftRecursive() && rule.isIRightRecursive() && !canBePostfix)) { + + lcond = or(lessEq(lprec, integer(0)), greaterEq(lprec, integer(prec + 1))); + rcond = greaterEq(integer(prec), pprec); + + if (this.parity == 2) + rarg = tuple(integer(prec + 1), integer(0)); + else + rarg = integer(prec + 1); + + if (config.hasPrefixBelow(prec_level.getLhs())) { + if (this.larity == 2) + ret = tuple(minimum(prec, rprec), integer(0)); + else + ret = minimum(prec, rprec); + } else { + if (this.larity == 2) + ret = tuple(integer(prec), integer(0)); + else + ret = integer(prec); + } + + } else if (rule.isLeftRecursive()) { + lcond = or(and(lessEq(lprec, integer(0)), + notEqual(lprec, neg(integer(prec)))), + greaterEq(lprec, integer(prec))); + rcond = greaterEq(integer(prec), pprec); + + if (this.larity == 2) + ret = tuple(neg(integer(prec)), integer(0)); + else + ret = neg(integer(prec)); + + } else { + + rcond = notEqual(pprec, integer(prec)); + + if (this.parity == 2) + rarg = tuple(integer(first ? 0 : prec), integer(0)); + else + rarg = integer(first ? 0 : prec); + + // larity == 1 + if (config.hasPrefixBelow(prec_level.getLhs())) { + if (this.larity == 2) + ret = tuple(minimum(prec, rprec), integer(0)); + else + ret = minimum(prec, rprec); + } else { + if (this.larity == 2) + ret = tuple(integer(prec), integer(0)); + else + ret = integer(prec); + } + } + + break; + case UNDEFINED: + + if (rule.isLeftRecursive() || rule.isILeftRecursive()) { + lcond = or(lessEq(lprec, integer(0)), greaterEq(lprec, integer(prec))); + rcond = greaterEq(integer(prec), pprec); + + if (rule.isILeftRecursive() && canBePrefix) { + lcond = or(equal(var("l"), undef()), lcond); + // TODO: should become post-condition + rcond = ifThenElse(equal(var("l"), undef()), TRUE, rcond); + } + } + + if (rule.isRightRecursive() || rule.isIRightRecursive()) { + if (this.parity == 2) + rarg = tuple(integer(first ? 0 : prec), integer(0)); + else + rarg = integer(first ? 0 : prec); + + // larity == 1 + if (config.hasPrefixBelow(prec_level.getLhs())) { + if (this.larity == 2) + ret = tuple(minimum(prec, rprec), integer(0)); + else + ret = minimum(prec, rprec); + + if (rule.isIRightRecursive() && canBePostfix) + ret = ifThenElse(equal(var("r"), undef()), + this.larity == 2 ? tuple(integer(0), integer(0)) : integer(0), + ret); + + } else { + if (this.larity == 2) + ret = tuple(integer(prec), integer(0)); + else + ret = integer(prec); + } + } else { + if (this.larity == 2) + ret = tuple(integer(0), integer(0)); + else + ret = integer(0); + } + break; + default: + throw new RuntimeException("Unexpected associativity: " + assoc); + } + + } + + if (assoc_group == null && prec_level.getLhs() != prec_level.getRhs()) { + + if (rule.isILeftRecursive() || rule.isIRightRecursive()) { + System.out.println("Warning: not yet implemented: indirect recursion inside a group of the same " + + "precedence with multiple rules: " + rule); + } + + // Local to an associativity group + int arity = config.groups.get(prec_level.getLhs()) == null ? 1 : config.groups.get(prec_level.getLhs()); + int larity = (arity == 2 || arity == 4) ? 2 : 1; + int parity = (arity == 3 || arity == 4) ? 2 : 1; + + if (rule.isLeftRecursive()) { + lcond = or(lessEq(lprec, integer(0)), greaterEq(lprec, integer(prec))); + rcond = greaterEq(integer(prec), pprec); + } + + if (rule.isRightRecursive()) { + if (parity == 2) + rarg = tuple(integer(first ? 0 : undefined), integer(0)); + else { + if (this.parity == 2) + rarg = tuple(integer(first ? 0 : undefined), integer(0)); + else + rarg = integer(first ? 0 : undefined); + } + + if (prec_level.hasPrefixUnaryBelow()) { + if (larity == 2) + ret = tuple(minimum(undefined, rprec), integer(0)); + else { + if (this.larity == 2) + ret = tuple(minimum(undefined, rprec), integer(0)); + else + ret = minimum(undefined, rprec); + } + } else { + if (larity == 2) + ret = tuple(integer(undefined), integer(0)); + else { + if (this.larity == 2) + ret = tuple(integer(undefined), integer(0)); + else + ret = integer(undefined); + } + } + } else { + if (this.larity == 2) + ret = tuple(integer(0), integer(0)); + else + ret = integer(0); + } + + if (rule.getAssociativity() != Associativity.UNDEFINED) { + + switch (rule.getAssociativity()) { + + case LEFT: + if (parity == 2) + rarg = tuple(integer(first ? 0 : undefined), integer(prec)); + else { + if (this.parity == 2) + rarg = tuple(integer(prec), integer(0)); + else + rarg = integer(prec); + } + + rcond = and(greaterEq(integer(prec), pprec), + notEqual(parity == 2 ? passoc : pprec, integer(prec))); + + break; + case RIGHT: + + if (prec_level.hasPrefixUnaryBelow()) { + if (larity == 2) + ret = tuple(minimum(prec, rprec), integer(0)); + else { + if (this.larity == 2) + ret = tuple(minimum(prec, rprec), integer(0)); + else + ret = minimum(prec, rprec); + } + } else { + if (larity == 2) + ret = tuple(integer(prec), integer(0)); + else { + if (this.larity == 2) + ret = tuple(integer(prec), integer(0)); + else + ret = integer(prec); + } + } + + lcond = or(lessEq(lprec, integer(0)), + and(notEqual(larity == 2 ? lassoc : lprec, integer(prec)), + greaterEq(lprec, integer(prec)))); + + break; + case NON_ASSOC: + + if (rule.isLeftRecursive() && rule.isRightRecursive()) { + + if (parity == 2) + rarg = tuple(integer(first ? 0 : undefined), integer(prec)); + else { + if (this.parity == 2) + rarg = tuple(integer(prec), integer(0)); + else + rarg = integer(prec); + } + + if (prec_level.hasPrefixUnaryBelow()) { + if (larity == 2) + ret = tuple(minimum(prec, rprec), integer(0)); + else { + if (this.larity == 2) + ret = tuple(minimum(prec, rprec), integer(0)); + else + ret = minimum(prec, rprec); + } + } else { + if (larity == 2) + ret = tuple(integer(prec), integer(0)); + else { + if (this.larity == 2) + ret = tuple(integer(prec), integer(0)); + else + ret = integer(prec); + } + } + + lcond = or(lessEq(lprec, integer(0)), + and(notEqual(larity == 2 ? lassoc : lprec, integer(prec)), + greaterEq(lprec, integer(prec)))); + + rcond = and(greaterEq(integer(prec), pprec), + notEqual(parity == 2 ? passoc : pprec, integer(prec))); + + } else if (rule.isLeftRecursive()) { + + if (larity == 2) { + ret = tuple(integer(0), neg(integer(prec))); + + lcond = and(or(lessEq(lprec, integer(0)), greaterEq(lprec, integer(prec))), + notEqual(lassoc, neg(integer(prec)))); + } else { + if (this.larity == 2) + ret = tuple(neg(integer(prec)), integer(0)); + else + ret = neg(integer(prec)); + + lcond = or(and(lessEq(lprec, integer(0)), + notEqual(lprec, neg(integer(prec)))), + greaterEq(lprec, integer(prec))); + } + + } else { + if (parity == 2) + rarg = tuple(integer(first ? 0 : undefined), integer(prec)); + else { + if (this.parity == 2) + rarg = tuple(integer(prec), integer(0)); + else + rarg = integer(prec); + } + + rcond = notEqual(parity == 2 ? passoc : pprec, integer(prec)); + } + + break; + + default: + break; + } + } + } + } + + private static Expression pr(int current, Integer[] indices, boolean prefix) { + if (prefix) { + if (indices.length == 0) + return var("l"); + + if (indices.length == 1) + return pr1(var("l"), integer(current), integer(indices[0] + 1)); + + List list = Arrays.asList(indices); + Collections.reverse(list); + + return pr2(var("l"), integer(current), + list.stream().map(i -> integer(i + 1)).toArray(Expression[]::new)); + + } else { + if (indices.length == 0) + return var("r"); + + if (indices.length == 1) + return pr1(var("r"), integer(current), integer(indices[0] + 1)); + + List list = Arrays.asList(indices); + Collections.reverse(list); + + return pr2(var("r"), integer(current), + list.stream().map(i -> integer(i + 1)).toArray(Expression[]::new)); + } + } + + private static Expression minimum(int precedence, Expression rprec) { + return ifThenElse(lessEq(rprec, integer(0)), + integer(precedence), + min(rprec, integer(precedence))); + } + + public RuntimeRule transform() { + + if (rule.getBody() == null) + return rule; + + List symbols = new ArrayList<>(); + RuntimeRule.Builder builder = null; + + String head = rule.getHead().getName(); + + boolean isLeftOrRightRecursiveNonterminal = leftOrRightRecursiveNonterminals.contains(head); + boolean isHeadWithLabeledRules = headsWithLabeledRules.containsKey(head); + + switch (config_op) { + case _1: + if (isLeftOrRightRecursiveNonterminal && isHeadWithLabeledRules) + builder = rule.copyBuilderButWithHead( + rule.getHead().copy().addParameters("l", "r", "_not").build()); + else if (isLeftOrRightRecursiveNonterminal) + builder = rule.copyBuilderButWithHead(rule.getHead().copy().addParameters("l", "r").build()); + else if (isHeadWithLabeledRules) + builder = rule.copyBuilderButWithHead(rule.getHead().copy().addParameters("_not").build()); + else builder = rule.copy(); + + builder = builder.setSymbols(symbols); + + int i = 0; + for (Symbol symbol : rule.getBody()) { + + if (i == 0) isFirst = true; + else isFirst = false; + + if (i == rule.getBody().size() - 1) isLast = true; + else isLast = false; + + Symbol sym = symbol.accept(this); + if (preconditions != null && i == 0) + symbols.add(sym.copy().addPreConditions(preconditions).build()); + else + symbols.add(sym); + i++; + } + + break; + case _2: + + if (isLeftOrRightRecursiveNonterminal && isHeadWithLabeledRules) + builder = rule.copyBuilderButWithHead(rule.getHead().copy().addParameters("p", "_not").build()); + else if (isLeftOrRightRecursiveNonterminal) + builder = rule.copyBuilderButWithHead(rule.getHead().copy().addParameters("p").build()); + else if (isHeadWithLabeledRules) + builder = rule.copyBuilderButWithHead(rule.getHead().copy().addParameters("_not").build()); + else builder = rule.copy(); + + boolean isIndirectEnd = false; + + for (String pend : config.pends) { + Configuration c = configs.get(pend); + if (c.leftEnds.contains(rule.getHead().getName()) + || c.rightEnds.contains(rule.getHead().getName())) { + isIndirectEnd = true; + break; + } + } + + if (isIndirectEnd) { + String[] parameters = new String[config.pends.size()]; + int n = 0; + for (int j = 0; j < config.pends.size(); j++) { + Configuration configOfEnd = configs.get(config.pends.get(j)); + if (configOfEnd.leftEnds.contains(rule.getHead().getName()) + || configOfEnd.rightEnds.contains(rule.getHead().getName())) { + parameters[j] = "p" + j; + n++; + } + } + + String[] params = new String[n]; + int j = 0; + for (String parameter : parameters) { + if (parameter != null) + params[j++] = parameter; + } + + if (isHeadWithLabeledRules) + builder = rule.copyBuilderButWithHead(rule.getHead().copy().addParameters(params) + .addParameters("_not").build()); + else + builder = rule.copyBuilderButWithHead(rule.getHead().copy().addParameters(params).build()); + } + + builder = builder.setSymbols(symbols); + + i = 0; + for (Symbol symbol : rule.getBody()) { + + if (i == 0) isFirst = true; + else isFirst = false; + + if (i == rule.getBody().size() - 1) isLast = true; + else isLast = false; + + Symbol sym = symbol.accept(this); + + Set preconditions = new LinkedHashSet<>(); + Set postconditions = new LinkedHashSet<>(); + + if (rcond != null && i == 0) + preconditions.add(DataDependentCondition.predicate(rcond)); + + if (xrcond != null && i == 0) + preconditions.add(DataDependentCondition.predicate(xrcond)); + + if (lcond != null && i == 0) + postconditions.add(DataDependentCondition.predicate(lcond)); + + if (xlcond != null && i == 0) + postconditions.add(DataDependentCondition.predicate(xlcond)); + + if (!preconditions.isEmpty() && !postconditions.isEmpty()) + symbols.add(sym.copy().addPreConditions(preconditions).addPostConditions(postconditions) + .build()); + else if (!postconditions.isEmpty()) + symbols.add(sym.copy().addPostConditions(postconditions).build()); + else if (!preconditions.isEmpty()) + symbols.add(sym.copy().addPreConditions(preconditions).build()); + else + symbols.add(sym); + + i++; + } + + Expression iret = null; + + if (isIndirectEnd) { + if (config.pends.size() == 1) { + if (lret[0] != null && rret[0] != null) + iret = tuple(lret[0], rret[0]); + else if (lret[0] != null) + iret = lret[0]; + else if (rret[0] != null) + iret = rret[0]; + } else { + int j = 0; + int size = 0; + + for (Expression l : lret) { + if (l != null || rret[j] != null) + size++; + j++; + } + + j = 0; + int k = 0; + Expression[] rets = new Expression[size]; + for (Expression l : lret) { + if (l != null && rret[j] != null) { + rets[k++] = tuple(l, rret[j]); + } else if (l != null) + rets[k++] = l; + else if (rret[j] != null) + rets[k++] = rret[j]; + j++; + } + if (rets.length != 0) + iret = tuple(rets); + } + } + + if (ret != null && xret != null) + symbols.add(Return.ret(tuple(ret, xret))); + else if (ret != null) + symbols.add(Return.ret(ret)); + else if (xret != null && iret == null) + symbols.add(Return.ret(xret)); + + if (iret != null && xret != null) + symbols.add(Return.ret(tuple(iret, xret))); + else if (iret != null) + symbols.add(Return.ret(iret)); + + break; + } + return builder.setDefinition(rule.getDefinition()).build(); + } + + @Override + public Symbol visit(Align symbol) { + Symbol sym = symbol.getSymbol().accept(this); + + return sym == symbol.getSymbol() ? symbol + : new Align.Builder(sym).setLabel(symbol.getLabel()).addConditions(symbol).build(); + } + + @Override + public Symbol visit(Block symbol) { + List symbols = symbol.getSymbols(); + Symbol[] syms = new Symbol[symbols.size()]; + + boolean isFirst = this.isFirst; + boolean isLast = this.isLast; + + int j = 0; + boolean modified = false; + for (Symbol sym : symbols) { + + if (isFirst && j == 0) this.isFirst = true; + else this.isFirst = false; + + if (isLast && j == symbols.size() - 1) this.isLast = true; + else this.isLast = false; + + syms[j] = sym.accept(this); + if (sym != syms[j]) + modified |= true; + j++; + } + + this.isFirst = isFirst; + this.isLast = isLast; + + return modified ? new Block.Builder(syms).setLabel(symbol.getLabel()).addConditions(symbol).build() + : symbol; + } + + @Override + public Symbol visit(Code symbol) { + Symbol sym = symbol.getSymbol().accept(this); + if (sym == symbol.getSymbol()) + return symbol; + + return new Code.Builder(sym, symbol.getStatements()).setLabel(symbol.getLabel()).addConditions(symbol) + .build(); + } + + @Override + public Symbol visit(Error error) { + return error; + } + + @Override + public Symbol visit(Conditional symbol) { + Symbol sym = symbol.getSymbol().accept(this); + if (sym == symbol.getSymbol()) + return symbol; + + return new Conditional.Builder(sym, symbol.getExpression()).setLabel(symbol.getLabel()).addConditions( + symbol).build(); + } + + @Override + public Symbol visit(IfThen symbol) { + Symbol sym = symbol.getThenPart().accept(this); + if (sym == symbol.getThenPart()) + return symbol; + + return new IfThen.Builder(symbol.getExpression(), sym).setLabel(symbol.getLabel()).addConditions(symbol) + .build(); + } + + @Override + public Symbol visit(IfThenElse symbol) { + Symbol thenPart = symbol.getThenPart().accept(this); + Symbol elsePart = symbol.getElsePart().accept(this); + if (thenPart == symbol.getThenPart() + && elsePart == symbol.getElsePart()) + return symbol; + + return new IfThenElse.Builder(symbol.getExpression(), thenPart, elsePart).setLabel(symbol.getLabel()) + .addConditions(symbol).build(); + } + + @Override + public Symbol visit(Ignore symbol) { + Symbol sym = symbol.getSymbol().accept(this); + + return sym == symbol.getSymbol() ? symbol + : new Ignore.Builder(sym).setLabel(symbol.getLabel()).addConditions(symbol).build(); + } + + @Override + public Symbol visit(Nonterminal symbol) { + // recursive symbol with precedence, i.e., it has parameters and needs arguments + boolean isUseOfLeftOrRight = leftOrRightRecursiveNonterminals.contains(symbol.getName()); + + Configuration config = configs.get(symbol.getName()); + // symbol that introduces indirection, i.e., it has parameters and needs arguments + boolean isUseOfLeftOrRightEnd = false; + + List pends = new ArrayList<>(); + + if (!config.pends.isEmpty()) { + for (String end : config.pends) + if (configs.get(end).leftEnds.contains(symbol.getName()) + || configs.get(end).rightEnds.contains(symbol.getName())) { + isUseOfLeftOrRightEnd = true; + pends.add(end); + } + } + + Map labels = headsWithLabeledRules.get(symbol.getName()); + + if (!isUseOfLeftOrRight && labels == null && !isUseOfLeftOrRightEnd) return symbol; + + boolean isRecursiveUseOfLeftOrRight = isUseOfLeftOrRight && symbol.getName().equals( + rule.getHead().getName()); + + boolean isIndirectRecursiveUseOfLeftOrRight = isUseOfLeftOrRightEnd && pends.contains( + rule.getHead().getName()); + + Expression _not = null; + Expression[] arguments = null; + + switch (config_op) { + case _1: + if (labels != null) { + int n = 0; + Set excepts = symbol.getExcepts(); + if (excepts != null) + for (String except : excepts) { + Integer i = labels.get(except); + + if (i == null) + throw new RuntimeException("Undeclared label: " + except); + + n += 1 << i; + } + + _not = integer(n); + } + + if (isUseOfLeftOrRight) + arguments = new Expression[]{integer(0), integer(0)}; + + if (isRecursiveUseOfLeftOrRight && isFirst) + arguments = new Expression[]{l1, r1}; + else if (isRecursiveUseOfLeftOrRight && isLast) + arguments = new Expression[]{l2, r2}; + + if (arguments != null && _not != null) + return symbol.copy().apply(arguments).apply(_not).build(); + else if (arguments != null) + return symbol.copy().apply(arguments).build(); + else + return symbol.copy().apply(_not).build(); + + case _2: + if (labels != null) { + if (rule.isLeftRecursive() && isFirst) { + _not = integer(0); + if (isRecursiveUseOfLeftOrRight) { + Configuration c = configs.get(rule.getHead().getName()); + if (c != null && !c.right_rec_rules.isEmpty()) { + + int n = 0; + for (Map.Entry entry : c.right_rec_rules.entrySet()) { + if (rule.getPrecedence() != -1 + && rule.getPrecedence() > entry.getValue().getPrecedenceLevel().getRhs()) { + n = 1 << labels.get(entry.getKey()); + } + } + _not = AST.shift(var("_not"), integer(n)); + } + } + } else { + int n = 0; + Set excepts = symbol.getExcepts(); + if (excepts != null) + for (String except : excepts) { + Integer i = labels.get(except); + + if (i == null) + throw new RuntimeException("Undeclared label: " + except); + + n += 1 << i; + } + + _not = integer(n); + } + } + + String variable = ""; + + if (isUseOfLeftOrRight) { + int parity = config.parity; + + if (config.leftEnds.contains(rule.getHead().getName()) + // TODO: if precedence also applies to X, use larg and rarg + || config.rightEnds.contains(rule.getHead().getName())) { + + List ends = configs.get(rule.getHead().getName()).pends; + + int i = ends.indexOf(symbol.getName()); + + boolean canBeFromLeft = config.leftEnds.contains(rule.getHead().getName()); + boolean canBeFromRight = config.rightEnds.contains(rule.getHead().getName()); + + if (isFirst && isLast) { // X ::= E + + if (canBeFromLeft && canBeFromRight) { + variable = "l"; + arguments = new Expression[]{var("p" + i)}; + + // TODO: if-then-else is needed for the return value + throw new RuntimeException("Unsupported yet!"); + + } else if (canBeFromLeft) { + variable = "l"; + arguments = new Expression[]{var("p" + i)}; + lret[i] = var("l"); + } else if (canBeFromRight) { + variable = "r"; + arguments = new Expression[]{var("p" + i)}; + boolean hasPrefixBelow = config.rightEndsPrefixBelow.contains( + rule.getHead().getName()); + boolean canBecomePostfix = config.rightEndsThatCanLeadToPostfix.contains( + rule.getHead().getName()); + if (hasPrefixBelow || canBecomePostfix) + rret[i] = var("r"); + else + variable = ""; + } else { + throw new RuntimeException("Shouldn't have happened!"); + } + + } else if (isFirst) { // X ::= E alpha + variable = "l"; + if (canBeFromLeft && canBeFromRight) + arguments = new Expression[]{get(var("p" + i), 0)}; + else if (canBeFromLeft) + arguments = new Expression[]{var("p" + i)}; + lret[i] = var("l"); + } else if (isLast) { // X ::= alpha E + variable = "r"; + if (canBeFromLeft && canBeFromRight) + arguments = new Expression[]{get(var("p" + i), 1)}; + else if (canBeFromRight) { + arguments = new Expression[]{var("p" + i)}; + + boolean hasPrefixBelow = config.rightEndsPrefixBelow.contains( + rule.getHead().getName()); + boolean canBecomePostfix = config.rightEndsThatCanLeadToPostfix.contains( + rule.getHead().getName()); + + if (hasPrefixBelow || canBecomePostfix) + rret[i] = var("r"); + else + variable = ""; + } + } else + arguments = new Expression[]{parity == 2 ? tuple(integer(0), integer(0)) : integer(0)}; + + } else + arguments = new Expression[]{parity == 2 ? tuple(integer(0), integer(0)) : integer(0)}; + } + + if (isUseOfLeftOrRightEnd) { // . ::= alpha X alpha + arguments = new Expression[config.pends.size()]; + int i = 0; + for (String end : config.pends) { + + if (configs.get(end).leftEnds.contains(symbol.getName()) + || configs.get(end).rightEnds.contains(symbol.getName())) { + + boolean canReachFromLeft = configs.get(end).leftEnds.contains(symbol.getName()); + boolean canReachFromRight = configs.get(end).rightEnds.contains(symbol.getName()); + + int parity = configs.get(end).parity; + + if (canReachFromLeft && canReachFromRight) + arguments[i] = tuple(parity == 2 ? tuple(integer(0), integer(0)) : integer(0), + parity == 2 ? tuple(integer(0), integer(0)) : integer(0)); + else + arguments[i] = parity == 2 ? tuple(integer(0), integer(0)) : integer(0); + i++; + } + } + } + + if (isRecursiveUseOfLeftOrRight && isFirst && isLast) // E ::= E + throw new RuntimeException("Cyclic use where precedence applies:" + rule); + + if (isRecursiveUseOfLeftOrRight && isFirst) { // E ::= E alpha + variable = "l"; + arguments = new Expression[]{larg}; + } else if (isRecursiveUseOfLeftOrRight && isLast) { // E ::= alpha E + variable = "r"; + arguments = new Expression[]{rarg}; + if (!config.hasPrefixBelow(rule.getPrecedenceLevel().getLhs())) + variable = ""; + } + + Statement binding = null; + Statement lbinding = null; + Statement rbinding = null; + + // TODO: if precedence also applies to X, propagate argument from E + if (isIndirectRecursiveUseOfLeftOrRight) { + + boolean canReachLeft = configs.get(rule.getHead().getName()).leftEnds.contains( + symbol.getName()); + boolean canReachRight = configs.get(rule.getHead().getName()).rightEnds.contains( + symbol.getName()); + + if (isFirst && isLast) { // E ::= X + if (pends.size() == 1) { + variable = symbol.getName().toLowerCase(); // x + if (canReachLeft && canReachRight) { + lbinding = varDeclStat("l", get(var(variable), 0)); + rbinding = varDeclStat("r", get(var(variable), 1)); + arguments = new Expression[]{tuple(larg, rarg)}; + } else if (canReachLeft) { + variable = "l"; + arguments = new Expression[]{larg}; + } else { + boolean prefixBelow = configs.get( + rule.getHead().getName()).rightEndsPrefixBelow.contains(symbol.getName()); + boolean canBecomePostfix = configs.get( + rule.getHead().getName()).rightEndsThatCanLeadToPostfix.contains( + symbol.getName()); + if (prefixBelow || canBecomePostfix) + variable = "r"; + else + variable = ""; + arguments = new Expression[]{rarg}; + } + } else { + int i = config.pends.indexOf(rule.getHead().getName()); + variable = symbol.getName().toLowerCase(); + if (canReachLeft && canReachRight) { + lbinding = varDeclStat("l", get(get(var(variable), i), 0)); // l = x.i.0 + rbinding = varDeclStat("r", get(get(var(variable), i), 1)); // r = x.i.1 + arguments[i] = tuple(larg, rarg); + } else if (canReachLeft) { + binding = varDeclStat("l", get(var(variable), i)); + arguments[i] = larg; + } else { + boolean prefixBelow = configs.get( + rule.getHead().getName()).rightEndsPrefixBelow.contains(symbol.getName()); + boolean canBecomePostfix = configs.get( + rule.getHead().getName()).rightEndsThatCanLeadToPostfix.contains( + symbol.getName()); + if (prefixBelow || canBecomePostfix) + binding = varDeclStat("r", get(var(variable), i)); + else + variable = ""; + arguments[i] = rarg; + } + } + } else if (isFirst) { // E ::= X aplha + if (pends.size() == 1) { + variable = "l" + symbol.getName().toLowerCase(); + if (canReachLeft && canReachRight) { + binding = varDeclStat("l", get(var(variable), 0)); + arguments = new Expression[]{tuple(larg, + config.parity == 2 ? tuple(integer(0), + integer(0)) : integer( + 0))}; + } else { + variable = "l"; + arguments = new Expression[]{larg}; + } + } else { + int i = config.pends.indexOf(rule.getHead().getName()); + variable = "l" + symbol.getName().toLowerCase(); + if (canReachLeft && canReachRight) { + binding = varDeclStat("l", get(get(var(variable), i), 0)); + arguments[i] = tuple(larg, + config.parity == 2 ? tuple(integer(0), integer(0)) : integer( + 0)); + } else { + binding = varDeclStat("l", get(var(variable), i)); + arguments[i] = larg; + } + } + } else if (isLast) { // E ::= alpha X + if (pends.size() == 1) { + + variable = "r" + symbol.getName().toLowerCase(); + boolean prefixBelow = configs.get( + rule.getHead().getName()).rightEndsPrefixBelow.contains(symbol.getName()); + boolean canBecomePostfix = configs.get( + rule.getHead().getName()).rightEndsThatCanLeadToPostfix.contains( + symbol.getName()); + + if (canReachLeft && canReachRight) { + if (prefixBelow || canBecomePostfix) + binding = varDeclStat("r", get(var(variable), 1)); + else + variable = ""; + arguments = new Expression[]{tuple( + config.parity == 2 ? tuple(integer(0), integer(0)) : integer(0), rarg)}; + } else { + if (prefixBelow || canBecomePostfix) + variable = "r"; + else + variable = ""; + arguments = new Expression[]{rarg}; + } + } else { + int i = config.pends.indexOf(rule.getHead().getName()); + variable = "r" + symbol.getName(); + boolean prefixBelow = configs.get( + rule.getHead().getName()).rightEndsPrefixBelow.contains(symbol.getName()); + boolean canBecomePostfix = configs.get( + rule.getHead().getName()).rightEndsThatCanLeadToPostfix.contains( + symbol.getName()); + + if (canReachLeft && canReachRight) { + if (prefixBelow || canBecomePostfix) + binding = varDeclStat("r", get(get(var(variable), i), 1)); + else + variable = ""; + arguments[i] = tuple( + config.parity == 2 ? tuple(integer(0), integer(0)) : integer(0), rarg); + } else { + if (prefixBelow || canBecomePostfix) + binding = varDeclStat("r", get(var(variable), i)); + else + variable = ""; + arguments[i] = rarg; + } + } + } + // TODO: if precedence also applies to X, use larg and rarg to propagate down + } else if (isUseOfLeftOrRightEnd) { + if (isFirst && isLast) { // X ::= Y + List ends = config.pends; + for (String end : ends) { + + if (configs.get(end).leftEnds.contains(symbol.getName()) + || configs.get(end).rightEnds.contains(symbol.getName())) { + + int i = ends.indexOf(end); + + // Note: shouldn't look up the left and right ends of the head + boolean canBeFromLeft = configs.get(end).leftEnds.contains(symbol.getName()); + boolean canBeFromRight = configs.get(end).rightEnds.contains(symbol.getName()); + + variable = symbol.getName().toLowerCase(); + arguments[i] = var("p" + i); + + if (canBeFromLeft && canBeFromRight) { + lret[config.pends.indexOf(end)] = ends.size() == 1 ? get(var(variable), + 0) : get( + get(var(variable), i), 0); + + boolean hasPrefixBelow = configs.get(end).rightEndsPrefixBelow.contains( + symbol.getName()); + boolean canBecomePostfix = configs.get( + end).rightEndsThatCanLeadToPostfix.contains(symbol.getName()); + + if (hasPrefixBelow || canBecomePostfix) + rret[config.pends.indexOf(end)] = ends.size() == 1 ? get(var(variable), + 1) : get( + get(var(variable), i), 1); + + } else if (canBeFromLeft) { + variable = "l"; + lret[config.pends.indexOf(end)] = ends.size() == 1 ? var(variable) : get( + var(variable), i); + } else { + variable = "r"; + boolean hasPrefixBelow = configs.get(end).rightEndsPrefixBelow.contains( + symbol.getName()); + boolean canBecomePostfix = configs.get( + end).rightEndsThatCanLeadToPostfix.contains(symbol.getName()); + + if (hasPrefixBelow || canBecomePostfix) + rret[config.pends.indexOf(end)] = ends.size() == 1 ? var(variable) : get( + var(variable), i); + else + variable = ""; + } + } + } + } else if (isFirst) { // X ::= Y alpha (includes X ::= X alpha) + List ends = config.pends; + for (String end : ends) { + + if (configs.get(end).leftEnds.contains(symbol.getName()) + || configs.get(end).rightEnds.contains(symbol.getName())) { + + int parity = configs.get(end).parity; + int i = ends.indexOf(end); + + // Note: shouldn't look up the left and right ends of the head + boolean canBeFromLeft = configs.get(end).leftEnds.contains( + rule.getHead().getName()); + boolean canBeFromRight = configs.get(end).rightEnds.contains( + rule.getHead().getName()); + + variable = "l"; + + if (canBeFromLeft && canBeFromRight) { + arguments[i] = tuple(get(var("p" + i), 0), + parity == 2 ? tuple(integer(0), integer(0)) : integer(0)); + lret[config.pends.indexOf(end)] = ends.size() == 1 ? get(var("l"), 0) : get( + get(var("l"), i), 0); + } else if (canBeFromLeft) { + arguments[i] = var("p" + i); + lret[config.pends.indexOf(end)] = ends.size() == 1 ? var("l") : get(var("l"), + i); + } else if (canBeFromRight) { + arguments[i] = parity == 2 ? tuple(integer(0), integer(0)) : integer(0); + variable = ""; + } + } + } + } else if (isLast) { // X ::= alpha Y (includes X ::= alpha X) + List ends = config.pends; + for (String end : ends) { + + if (configs.get(end).leftEnds.contains(symbol.getName()) + || configs.get(end).rightEnds.contains(symbol.getName())) { + + int parity = configs.get(end).parity; + int i = ends.indexOf(end); + + // Note: shouldn't look up the left and right ends of the head + boolean canBeFromLeft = configs.get(end).leftEnds.contains( + rule.getHead().getName()); + boolean canBeFromRight = configs.get(end).rightEnds.contains( + rule.getHead().getName()); + + variable = "r"; + + if (canBeFromLeft && canBeFromRight) { + arguments[i] = tuple(parity == 2 ? tuple(integer(0), integer(0)) : integer(0), + get(var("p" + i), 1)); + + boolean hasPrefixBelow = configs.get(end).rightEndsPrefixBelow.contains( + symbol.getName()); + boolean canBecomePostfix = configs.get( + end).rightEndsThatCanLeadToPostfix.contains(symbol.getName()); + + if (hasPrefixBelow || canBecomePostfix) + rret[config.pends.indexOf(end)] = ends.size() == 1 ? get(var("r"), 1) : get( + get(var("r"), i), 1); + else + variable = ""; + } else if (canBeFromRight) { + + arguments[i] = var("p" + i); + + boolean hasPrefixBelow = configs.get(end).rightEndsPrefixBelow.contains( + symbol.getName()); + boolean canBecomePostfix = configs.get( + end).rightEndsThatCanLeadToPostfix.contains(symbol.getName()); + + if (hasPrefixBelow || canBecomePostfix) + rret[config.pends.indexOf(end)] = ends.size() == 1 ? var("r") : get( + var("r"), i); + else + variable = ""; + } else if (canBeFromLeft) { + arguments[i] = parity == 2 ? tuple(integer(0), integer(0)) : integer(0); + variable = ""; + } + } + } + } + } + + // Case of a left-recursive nonterminal that does not specify precedence + if (labels != null && symbol.getExcepts() != null && rule.isLeftRecursive() && isFirst) + variable = "l"; + + Symbol newone = null; + + if (arguments != null) { + + int n = 0; + for (Expression argument : arguments) + if (argument != null) + n++; + + Expression[] args = new Expression[n]; + + n = 0; + for (Expression argument : arguments) + if (argument != null) { + args[n] = argument; + n++; + } + + arguments = args; + } + + if (arguments != null && _not != null) + newone = variable.isEmpty() ? symbol.copy().apply(arguments).apply(_not).build() + : symbol.copy().apply(arguments).apply(_not).setVariable(variable) + .build(); + else if (arguments != null) { + newone = variable.isEmpty() ? symbol.copy().apply(arguments).build() + : symbol.copy().apply(arguments).setVariable(variable).build(); + } else + newone = variable.isEmpty() ? symbol.copy().apply(_not).build() + : symbol.copy().apply(_not).setVariable(variable).build(); + if (binding != null) + return Code.code(newone, binding); + else if (lbinding != null && rbinding != null) { + return Code.code(newone, lbinding, rbinding); + } else + return newone; + } + + return symbol; + } + + @Override + public Symbol visit(Offside symbol) { + Symbol sym = symbol.getSymbol().accept(this); + + return sym == symbol.getSymbol() ? symbol + : new Offside.Builder(sym).setLabel(symbol.getLabel()).addConditions(symbol).build(); + } + + @Override + public Symbol visit(Terminal symbol) { + return symbol; + } + + @Override + public Symbol visit(While symbol) { + Symbol body = symbol.getBody().accept(this); + if (body == symbol.getBody()) + return symbol; + + return new While.Builder(symbol.getExpression(), body).setLabel(symbol.getLabel()).addConditions(symbol) + .build(); + } + + @Override + public Symbol visit(Return symbol) { + return symbol; + } + + @Override + public Symbol visit(Alt symbol) { + throw new RuntimeException("TODO: Unsupported EBNF while desugaring > and assoc!"); + } + + @Override + public Symbol visit(Opt symbol) { + throw new RuntimeException("TODO: Unsupported EBNF while desugaring > and assoc!"); + } + + @Override + public Symbol visit(Plus symbol) { + throw new RuntimeException("TODO: Unsupported EBNF while desugaring > and assoc!"); + } + + @Override + public Symbol visit(Group symbol) { + throw new RuntimeException("TODO: Unsupported EBNF while desugaring > and assoc!"); + } + + @Override + public Symbol visit(Star symbol) { + throw new RuntimeException("TODO: Unsupported EBNF while desugaring > and assoc!"); + } + + @Override + public Symbol visit(Start start) { + return start; +// return start.getNonterminal().accept(this); + } + + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/grammar/transformation/DesugarStartSymbol.java b/benchmarks/src/main/kotlin/org/iguana/grammar/transformation/DesugarStartSymbol.java new file mode 100644 index 000000000..91f668e35 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/grammar/transformation/DesugarStartSymbol.java @@ -0,0 +1,47 @@ +package org.iguana.grammar.transformation; + +import org.iguana.grammar.runtime.PrecedenceLevel; +import org.iguana.grammar.runtime.Recursion; +import org.iguana.grammar.runtime.RuntimeGrammar; +import org.iguana.grammar.runtime.RuntimeRule; +import org.iguana.grammar.slot.NonterminalNodeType; +import org.iguana.grammar.symbol.Associativity; +import org.iguana.grammar.symbol.Nonterminal; +import org.iguana.grammar.symbol.Start; + +import java.util.List; + +public class DesugarStartSymbol implements GrammarTransformation { + + private final List starts; + + public DesugarStartSymbol(List starts) { + this.starts = starts; + } + + @Override + public RuntimeGrammar transform(RuntimeGrammar grammar) { + RuntimeGrammar.Builder builder = new RuntimeGrammar.Builder(grammar); + + for (Start start : starts) { + Nonterminal startNonterminal = new Nonterminal.Builder(start.getName()).setNodeType( + NonterminalNodeType.Start).build(); + + RuntimeRule startRule = RuntimeRule.withHead(startNonterminal) + // TODO: For now we use the label top, but would be good to allow configuring it. + .addSymbol(new Nonterminal.Builder(start.getStartSymbol()).setLabel("top").build()) + .setRecursion(Recursion.NON_REC) + .setAssociativity(Associativity.UNDEFINED) + .setPrecedence(-1) + .setPrecedenceLevel(PrecedenceLevel.getFirstAndDone()) + .setDefinition(start) + .build(); + + builder.addRule(startRule); + builder.addStartSymbol(start); + } + + builder.setGlobals(grammar.getGlobals()); + return builder.build(); + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/grammar/transformation/DesugarState.java b/benchmarks/src/main/kotlin/org/iguana/grammar/transformation/DesugarState.java new file mode 100644 index 000000000..f4a57dacc --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/grammar/transformation/DesugarState.java @@ -0,0 +1,436 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.grammar.transformation; + +import org.iguana.datadependent.ast.AST; +import org.iguana.datadependent.ast.Expression; +import org.iguana.datadependent.traversal.FreeVariableVisitor; +import org.iguana.grammar.exception.UnexpectedSymbolException; +import org.iguana.grammar.operations.ReachabilityGraph; +import org.iguana.grammar.runtime.RuntimeGrammar; +import org.iguana.grammar.runtime.RuntimeRule; +import org.iguana.grammar.symbol.Align; +import org.iguana.grammar.symbol.Alt; +import org.iguana.grammar.symbol.Block; +import org.iguana.grammar.symbol.Code; +import org.iguana.grammar.symbol.Conditional; +import org.iguana.grammar.symbol.Error; +import org.iguana.grammar.symbol.Group; +import org.iguana.grammar.symbol.IfThen; +import org.iguana.grammar.symbol.IfThenElse; +import org.iguana.grammar.symbol.Ignore; +import org.iguana.grammar.symbol.Nonterminal; +import org.iguana.grammar.symbol.Nonterminal.Builder; +import org.iguana.grammar.symbol.Offside; +import org.iguana.grammar.symbol.Opt; +import org.iguana.grammar.symbol.Plus; +import org.iguana.grammar.symbol.Return; +import org.iguana.grammar.symbol.Star; +import org.iguana.grammar.symbol.Start; +import org.iguana.grammar.symbol.Symbol; +import org.iguana.grammar.symbol.Terminal; +import org.iguana.grammar.symbol.While; +import org.iguana.traversal.ISymbolVisitor; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static org.iguana.utils.string.StringUtil.listToString; + +/** + * @author Anastasia Izmaylova + */ + +/* + * phase: after EBNF transformation + */ +public class DesugarState implements GrammarTransformation { + + private final Map> uses = new HashMap<>(); + private final Map> updates = new HashMap<>(); + + private final Map> returns = new HashMap<>(); + private final Map>>> bindings = new HashMap<>(); + private RuntimeGrammar grammar; + + @Override + public RuntimeGrammar transform(RuntimeGrammar grammar) { + this.grammar = grammar; + Set current_uses; + Set current_updates; + for (Nonterminal head : grammar.getNonterminals()) { + + current_uses = new HashSet<>(); + current_updates = new HashSet<>(); + + uses.put(head, current_uses); + updates.put(head, current_updates); + + FreeVariableVisitor visitor = new FreeVariableVisitor(current_uses, current_updates); + + for (RuntimeRule rule : grammar.getAlternatives(head)) + visitor.compute(rule); + + if (!current_updates.isEmpty()) System.out.println( + "Updates: " + head + " " + listToString(current_updates, " , ")); + } + + Map> reachabilityGraph = new ReachabilityGraph(grammar).getReachabilityGraph(); + for (Map.Entry> entry : reachabilityGraph.entrySet()) { + current_uses = uses.get(entry.getKey()); + current_updates = updates.get(entry.getKey()); + + for (Nonterminal nonterminal : entry.getValue()) { + current_uses.addAll(uses.get(nonterminal)); + current_updates.addAll(updates.get(nonterminal)); + } + } + + // Compute returns + for (Nonterminal nonterminal : updates.keySet()) + returns.put(nonterminal, new HashSet<>()); + + for (Nonterminal head : grammar.getNonterminals()) { + FreeVariableVisitor visitor = new FreeVariableVisitor(uses, updates, returns); + + List>> nonterminal_bindings = new ArrayList<>(); + bindings.put(head, nonterminal_bindings); + + for (RuntimeRule rule : grammar.getAlternatives(head)) + nonterminal_bindings.add(visitor.computeBindings(rule)); + } + + boolean changed = true; + while (changed) { + changed = false; + for (Map.Entry> head : returns.entrySet()) { + if (!head.getValue().isEmpty()) { + for (Map> rule_bindings : bindings.get(head.getKey())) { + for (Map.Entry> in_rule_bindings : rule_bindings.entrySet()) { + Set nonterminal_updates = updates.get(in_rule_bindings.getKey()); + Set nonterminal_returns = returns.get(in_rule_bindings.getKey()); + for (String ret : head.getValue()) { + if (nonterminal_updates.contains(ret) && !in_rule_bindings.getValue().contains(ret)) { + in_rule_bindings.getValue().add(ret); + if (!nonterminal_returns.contains(ret)) { + nonterminal_returns.add(ret); + changed = true; + } + } + } + } + } + } + } + } + + for (Map.Entry> entry : uses.entrySet()) + if (!entry.getValue().isEmpty()) { + System.out.println("Uses: " + entry.getKey() + " " + listToString(entry.getValue(), ";")); + } + + for (Map.Entry> entry : returns.entrySet()) + if (!entry.getValue().isEmpty()) { + System.out.println("Returns: " + entry.getKey() + " " + listToString(entry.getValue(), ";")); + } + + Set newRules = new LinkedHashSet<>(); + for (Nonterminal nonterminal : grammar.getNonterminals()) { + int i = 0; + List>> nonterminal_bindings = bindings.get(nonterminal); + for (RuntimeRule rule : grammar.getAlternatives(nonterminal)) { + newRules.add(transform(rule, + uses, + nonterminal_bindings == null ? new HashMap<>() : nonterminal_bindings.get(i++), + returns)); + } + } + return RuntimeGrammar.builder() + .addRules(newRules) + .setLayout(grammar.getLayout()) + .setStartSymbols(grammar.getStartSymbols()) + .setEbnfLefts(grammar.getEBNFLefts()) + .setEbnfRights(grammar.getEBNFRights()) + .setGlobals(grammar.getGlobals()) + .setRegularExpressionDefinitions(grammar.getRegularExpressionDefinitions()) + .build(); + } + + private RuntimeRule transform( + RuntimeRule rule, + Map> uses, + Map> bindings, + Map> returns) { + if (rule.getBody() == null) return rule; + + Set head_uses = uses.get(rule.getHead()); + + RuntimeRule.Builder builder = null; + if (!head_uses.isEmpty()) { + String[] parameters = new String[head_uses.size()]; + int i = 0; + for (String parameter : head_uses) + parameters[i++] = parameter; + + builder = rule.copyBuilderButWithHead(rule.getHead().copy().addParameters(parameters).build()); + } + + if (builder == null) builder = rule.copy(); + + List symbols = new ArrayList<>(); + + builder = builder.setSymbols(symbols); + + DesugarStateVisitor visitor = new DesugarStateVisitor(uses, returns, bindings, grammar); + + Return rsym = null; + for (Symbol symbol : rule.getBody()) { + if (symbol instanceof Return) rsym = (Return) symbol; + else symbols.add(symbol.accept(visitor)); + } + + Set rets = returns.get(rule.getHead()); + + if (rets != null && !rets.isEmpty()) { + Expression[] exps; + int i = 0; + if (rsym != null) { + exps = new Expression[rets.size() + 1]; + exps[i++] = rsym.getExpression(); + } else { + exps = new Expression[rets.size()]; + } + + for (String ret : rets) + exps[i++] = AST.var(ret); + + symbols.add(Return.ret(AST.tuple(exps))); + } else { + if (rsym != null) symbols.add(rsym); + } + + return builder.build(); + } + + public static class DesugarStateVisitor implements ISymbolVisitor { + + private final Map> uses; + private final Map> returns; + private final Map> bindings; + private final RuntimeGrammar grammar; + + DesugarStateVisitor( + Map> uses, + Map> returns, + Map> bindings, + RuntimeGrammar grammar) { + this.uses = uses; + this.returns = returns; + this.bindings = bindings; + this.grammar = grammar; + } + + @Override + public Symbol visit(Align symbol) { + Symbol sym = symbol.getSymbol().accept(this); + if (sym == symbol.getSymbol()) return symbol; + return new Align.Builder(sym).setLabel(symbol.getLabel()).addConditions(symbol).build(); + } + + @Override + public Symbol visit(Block symbol) { + throw new UnexpectedSymbolException(symbol, "desugar-state"); + } + + @Override + public Symbol visit(Code symbol) { + Symbol sym = symbol.getSymbol().accept(this); + if (sym == symbol.getSymbol()) return symbol; + + return new Code.Builder(sym, symbol.getStatements()) + .setLabel(symbol.getLabel()) + .addConditions(symbol) + .build(); + } + + @Override + public Symbol visit(Error error) { + return error; + } + + @Override + public Symbol visit(Conditional symbol) { + Symbol sym = symbol.getSymbol().accept(this); + if (sym == symbol.getSymbol()) return symbol; + return new Conditional.Builder(sym, symbol.getExpression()) + .setLabel(symbol.getLabel()) + .addConditions(symbol) + .build(); + } + + @Override + public Symbol visit(IfThen symbol) { + throw new UnexpectedSymbolException(symbol, "desugar-state"); + } + + @Override + public Symbol visit(IfThenElse symbol) { + throw new UnexpectedSymbolException(symbol, "desugar-state"); + } + + @Override + public Symbol visit(Ignore symbol) { + Symbol sym = symbol.getSymbol().accept(this); + if (sym == symbol.getSymbol()) return symbol; + return new Ignore.Builder(sym).setLabel(symbol.getLabel()).addConditions(symbol).build(); + } + + @Override + public Symbol visit(Nonterminal symbol) { + + Builder builder = symbol.copy(); + + boolean changed = false; + + Set pass = uses.get(symbol); + + if (pass != null && !pass.isEmpty()) { + + Expression[] arguments = new Expression[pass.size()]; + int i = 0; + for (String argument : pass) + arguments[i++] = AST.var(argument); + + builder.apply(arguments); + changed = true; + } + + Set bind = bindings.get(symbol); + + if (bind != null) { + Set state = new LinkedHashSet<>(); + + Set stateReturn = returns.get(symbol); + if (stateReturn.size() > 0) { + // We should put _ as placeholders for other values returned by the nonterminal. + // This is particularly visible in mixing state threading and operator precedence. + // As operator precedence adds a return value, and that return value should be ignored here. + // See state/Test4 and state/Test5 for examples. + int count = getReturnSize(symbol) - (symbol.getVariable() == null ? 0 : 1); + for (int i = 0; i < count; i++) { + state.add("_"); + } + } + + for (String ret : stateReturn) { + if (bind.contains(ret)) state.add(ret); + else state.add("_"); + } + + if (!state.isEmpty()) { + builder.setState(state); + changed = true; + } + } + + return changed ? builder.build() : symbol; + } + + // Returns 1 if the grammar already returns a value + private int getReturnSize(Nonterminal symbol) { + List runtimeRules = grammar.getAlternatives(symbol); + if (runtimeRules.isEmpty() || runtimeRules.get(0).size() == 0) { + return 0; + } + Symbol lastSymbol = runtimeRules.get(0).getLastSymbol(); + if (lastSymbol instanceof Return) { + return 1; + } + return 0; + } + + @Override + public Symbol visit(Offside symbol) { + Symbol sym = symbol.getSymbol().accept(this); + if (sym == symbol.getSymbol()) return symbol; + return new Offside.Builder(sym).setLabel(symbol.getLabel()).addConditions(symbol).build(); + } + + @Override + public Symbol visit(Terminal symbol) { + return symbol; + } + + @Override + public Symbol visit(While symbol) { + throw new UnexpectedSymbolException(symbol, "desugar-state"); + } + + @Override + public Symbol visit(Return symbol) { + return symbol; + } + + @Override + public Symbol visit(Alt symbol) { + throw new UnexpectedSymbolException(symbol, "desugar-state"); + } + + @Override + public Symbol visit(Opt symbol) { + throw new UnexpectedSymbolException(symbol, "desugar-state"); + } + + @Override + public Symbol visit(Plus symbol) { + throw new UnexpectedSymbolException(symbol, "desugar-state"); + } + + @Override + public Symbol visit(Group symbol) { + throw new UnexpectedSymbolException(symbol, "desugar-state"); + } + + @Override + public Symbol visit(Star symbol) { + throw new UnexpectedSymbolException(symbol, "desugar-state"); + } + + @Override + public Symbol visit(Start symbol) { + throw new UnexpectedSymbolException(symbol, "desugar-state"); + } + + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/grammar/transformation/EBNFToBNF.java b/benchmarks/src/main/kotlin/org/iguana/grammar/transformation/EBNFToBNF.java new file mode 100644 index 000000000..e0edbfb08 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/grammar/transformation/EBNFToBNF.java @@ -0,0 +1,644 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.grammar.transformation; + +import org.iguana.datadependent.ast.AST; +import org.iguana.datadependent.ast.Expression; +import org.iguana.datadependent.traversal.FreeVariableVisitor; +import org.iguana.grammar.condition.DataDependentCondition; +import org.iguana.grammar.runtime.PrecedenceLevel; +import org.iguana.grammar.runtime.Recursion; +import org.iguana.grammar.runtime.RuntimeGrammar; +import org.iguana.grammar.runtime.RuntimeRule; +import org.iguana.grammar.slot.NonterminalNodeType; +import org.iguana.grammar.symbol.Align; +import org.iguana.grammar.symbol.Alt; +import org.iguana.grammar.symbol.Associativity; +import org.iguana.grammar.symbol.Block; +import org.iguana.grammar.symbol.Code; +import org.iguana.grammar.symbol.Conditional; +import org.iguana.grammar.symbol.Error; +import org.iguana.grammar.symbol.Group; +import org.iguana.grammar.symbol.IfThen; +import org.iguana.grammar.symbol.IfThenElse; +import org.iguana.grammar.symbol.Ignore; +import org.iguana.grammar.symbol.LayoutStrategy; +import org.iguana.grammar.symbol.Nonterminal; +import org.iguana.grammar.symbol.Nonterminal.Builder; +import org.iguana.grammar.symbol.Offside; +import org.iguana.grammar.symbol.Opt; +import org.iguana.grammar.symbol.Plus; +import org.iguana.grammar.symbol.Return; +import org.iguana.grammar.symbol.Star; +import org.iguana.grammar.symbol.Start; +import org.iguana.grammar.symbol.Symbol; +import org.iguana.grammar.symbol.Terminal; +import org.iguana.grammar.symbol.While; +import org.iguana.traversal.ISymbolVisitor; + +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * + * + * @author Ali Afroozeh + * @author Anastasia Izmaylova + * + */ +public class EBNFToBNF implements GrammarTransformation { + + private Map> ebnfLefts; + private Map> ebnfRights; + + @Override + public RuntimeGrammar transform(RuntimeGrammar grammar) { + Set newRules = new LinkedHashSet<>(); + ebnfLefts = grammar.getEBNFLefts(); + ebnfRights = grammar.getEBNFRights(); + grammar.getRules().forEach(r -> newRules.addAll(transform(r))); + return RuntimeGrammar.builder().addRules(newRules) + .addEBNFl(grammar.getEBNFLefts()) + .addEBNFr(grammar.getEBNFRights()) + .setLayout(grammar.getLayout()) + .setGlobals(grammar.getGlobals()) + .setRegularExpressionDefinitions(grammar.getRegularExpressionDefinitions()) + .setStartSymbols(grammar.getStartSymbols()).build(); + } + + private Set transform(RuntimeRule rule) { + Set newRules = new LinkedHashSet<>(); + newRules.add(rewrite(rule, newRules)); + return newRules; + } + + public RuntimeRule rewrite(RuntimeRule rule, Set newRules) { + + if (rule.getBody() == null) + return rule; + + RuntimeRule.Builder builder = new RuntimeRule.Builder(rule.getHead()); + + Set state = new HashSet<>(); + new FreeVariableVisitor(state).compute(rule); + + EBNFVisitor ebnf_visitor = new EBNFVisitor(state, newRules, rule.getLayout(), rule.getLayoutStrategy(), + ebnfLefts, ebnfRights); + + for (Symbol s : rule.getBody()) + builder.addSymbol(s.accept(ebnf_visitor)); + + return builder.setLayout(rule.getLayout()) + .setLayoutStrategy(rule.getLayoutStrategy()) + .setRecursion(rule.getRecursion()) + .setAssociativity(rule.getAssociativity()) + .setAssociativityGroup(rule.getAssociativityGroup()) + .setPrecedence(rule.getPrecedence()) + .setPrecedenceLevel(rule.getPrecedenceLevel()) + .setiRecursion(rule.getIRecursion()) + .setLeftEnd(rule.getLeftEnd()) + .setRightEnd(rule.getRightEnd()) + .setLeftEnds(rule.getLeftEnds()) + .setRightEnds(rule.getRightEnds()) + .setLabel(rule.getLabel()) + .setAttributes(rule.getAttributes()) + .setDefinition(rule.getDefinition()) + .build(); + } + + public static String getName(Symbol symbol, List separators, Symbol layout) { + if (separators.isEmpty() && layout == null) { + return symbol.getName(); + } else { + return "{" + symbol.getName() + + " " + separators.stream().map(Symbol::getName).collect(Collectors.joining(" ")) + + (layout == null ? "" : " " + layout) + + "}"; + } + } + + private static class EBNFVisitor implements ISymbolVisitor { + + private static int counter = 0; + + private final Set state; + private final Set addedRules; + private final Symbol layout; + private final LayoutStrategy strategy; + + private final Map> ebnfLefts; + private final Map> ebnfRights; + + /** + * Target cases: + */ + private Set freeVars; + private FreeVariableVisitor visitor; + + EBNFVisitor( + Set state, + Set addedRules, + Symbol layout, + LayoutStrategy strategy, + Map> ebnfLefts, + Map> ebnfRights) { + this.state = state; + this.addedRules = addedRules; + this.layout = layout; + this.strategy = strategy; + this.ebnfLefts = ebnfLefts; + this.ebnfRights = ebnfRights; + } + + private void init() { + freeVars = new LinkedHashSet<>(); + visitor = new FreeVariableVisitor(freeVars); + } + + /** + * (A | B) ::= A + * | B + */ + @Override + public Symbol visit(Alt symbol) { + List symbols = symbol.getSymbols().stream().map(x -> x.accept(this)).collect(Collectors.toList()); + + init(); + String[] parameters = null; + Expression[] arguments = null; + + new Alt.Builder(symbols).build().accept(visitor); + + if (!freeVars.isEmpty()) { + freeVars.removeAll(state); + parameters = freeVars.toArray(new String[0]); + arguments = freeVars.stream().map(v -> AST.var(v)).toArray(Expression[]::new); + } + + Nonterminal newNt = parameters == null ? new Nonterminal.Builder(symbol.getName()).setNodeType( + NonterminalNodeType.Alt).build() + : new Nonterminal.Builder(symbol.getName()).addParameters(parameters).setNodeType( + NonterminalNodeType.Alt).build(); + + symbols.forEach(x -> addedRules.add(RuntimeRule.withHead(newNt).addSymbol(x).setLayout(layout) + .setLayoutStrategy(strategy) + .setRecursion(Recursion.NON_REC).setAssociativity(Associativity.UNDEFINED) + .setPrecedence(-1).setPrecedenceLevel(PrecedenceLevel.getFirstAndDone()) + .setDefinition(symbol) + .build())); + + Builder copyBuilder = arguments == null ? newNt.copy() : newNt.copy().apply(arguments); + return copyBuilder.addConditions(symbol).setLabel(symbol.getLabel()).build(); + } + + /** + * S? ::= S + * | epsilon + */ + @Override + public Symbol visit(Opt symbol) { + Symbol in = symbol.getSymbol().accept(this); + + init(); + String[] parameters = null; + Expression[] arguments = null; + + in.setEmpty(); + visitor.visitSymbol(in); + + if (!freeVars.isEmpty()) { + freeVars.removeAll(state); + parameters = freeVars.toArray(new String[0]); + arguments = freeVars.stream().map(v -> AST.var(v)).toArray(Expression[]::new); + } + + Nonterminal newNt = parameters == null ? new Nonterminal.Builder(symbol.getName()).setNodeType( + NonterminalNodeType.Opt).build() + : new Nonterminal.Builder(symbol.getName()).addParameters(parameters).setNodeType( + NonterminalNodeType.Opt).build(); + + addedRules.add(RuntimeRule.withHead(newNt).addSymbol(in).setLayout(layout).setLayoutStrategy(strategy) + .setRecursion(Recursion.NON_REC).setAssociativity(Associativity.UNDEFINED) + .setPrecedence(-1).setPrecedenceLevel(PrecedenceLevel.getFirstAndDone()) + .setDefinition(symbol) + .build()); + addedRules.add(RuntimeRule.withHead(newNt) + .setRecursion(Recursion.NON_REC).setAssociativity(Associativity.UNDEFINED) + .setPrecedence(-1).setPrecedenceLevel(PrecedenceLevel.getFirstAndDone()) + .setDefinition(symbol) + .build()); + + Builder copyBuilder = arguments == null ? newNt.copy() : newNt.copy().apply(arguments); + return copyBuilder.addConditions(symbol).setLabel(symbol.getLabel()).build(); + } + + /** + * S+ ::= S+ S + * | S + * + * in case of separators: + * + * (S sep)+ ::= (S sep)+ sep S + * | S + * + */ + @Override + public Symbol visit(Plus symbol) { + Symbol S = symbol.getSymbol().accept(this); + + init(); + String[] parameters = null; + Expression[] arguments = null; + + S.setEmpty(); + visitor.visitSymbol(S); + + if (!freeVars.isEmpty()) { + freeVars.removeAll(state); + parameters = freeVars.toArray(new String[0]); + arguments = freeVars.stream().map(v -> AST.var(v)).toArray(Expression[]::new); + } + + List seperators = symbol.getSeparators().stream().map(sep -> sep.accept(this)).collect( + Collectors.toList()); + + Nonterminal newNt = parameters == null ? new Nonterminal.Builder( + getName(S, symbol.getSeparators(), layout) + "+").setNodeType(NonterminalNodeType.Plus).build() + : new Nonterminal.Builder(getName(S, symbol.getSeparators(), layout) + "+").addParameters(parameters) + .setNodeType(NonterminalNodeType.Plus).build(); + + addedRules.add(RuntimeRule.withHead(newNt) + .addSymbol(arguments != null ? new Nonterminal.Builder(newNt).apply(arguments).build() : newNt) + .addSymbols(seperators) + .addSymbols(S).setLayout(layout).setLayoutStrategy(strategy) + .setRecursion(Recursion.LEFT_REC).setAssociativity(Associativity.UNDEFINED) + .setPrecedence(1).setPrecedenceLevel(PrecedenceLevel.getFirstAndDone()) + .setLeftEnd(newNt.getName()) + .setRightEnd(S.getName()) + .setLeftEnds(ebnfLefts.containsKey(newNt.getName()) ? ebnfLefts.get(newNt.getName()) : new HashSet<>()) + .setRightEnds( + ebnfRights.containsKey(newNt.getName()) ? ebnfRights.get(newNt.getName()) : new HashSet<>()) + .setDefinition(symbol) + .build()); + addedRules.add(RuntimeRule.withHead(newNt).addSymbol(S) + .setRecursion(Recursion.NON_REC).setAssociativity(Associativity.UNDEFINED) + .setPrecedence(-1).setPrecedenceLevel(PrecedenceLevel.getFirstAndDone()) + .setLeftEnd(S.getName()) + .setRightEnd(S.getName()) + .setLeftEnds(ebnfLefts.containsKey(newNt.getName()) ? ebnfLefts.get(newNt.getName()) : new HashSet<>()) + .setRightEnds( + ebnfRights.containsKey(newNt.getName()) ? ebnfRights.get(newNt.getName()) : new HashSet<>()) + .setDefinition(symbol) + .build()); + + Builder copyBuilder = arguments == null ? newNt.copy() : newNt.copy().apply(arguments); + return copyBuilder.addConditions(symbol).setLabel(symbol.getLabel()).build(); + } + + /** + * (S) ::= S + */ + @Override + public Symbol visit(Group symbol) { + List symbols = symbol.getSymbols().stream().map(x -> x.accept(this)).collect(Collectors.toList()); + + init(); + String[] parameters = null; + Expression[] arguments = null; + + new Group.Builder(symbols).build().accept(visitor); + + if (!freeVars.isEmpty()) { + freeVars.removeAll(state); + parameters = freeVars.toArray(new String[0]); + arguments = freeVars.stream().map(v -> AST.var(v)).toArray(Expression[]::new); + } + + Nonterminal newNt = parameters == null ? new Nonterminal.Builder(symbol.getName()).setNodeType( + NonterminalNodeType.Seq).build() + : new Nonterminal.Builder(symbol.getName()).addParameters(parameters).setNodeType( + NonterminalNodeType.Seq).build(); + + addedRules.add(RuntimeRule.withHead(newNt).addSymbols(symbols).setLayout(layout).setLayoutStrategy(strategy) + .setRecursion(Recursion.NON_REC).setAssociativity(Associativity.UNDEFINED) + .setPrecedence(-1).setPrecedenceLevel(PrecedenceLevel.getFirstAndDone()) + .setLeftEnd(symbols.get(0).getName()) + .setRightEnd(symbols.get(symbols.size() - 1).getName()) + .setLeftEnds(ebnfLefts.containsKey(newNt.getName()) ? ebnfLefts.get(newNt.getName()) : new HashSet<>()) + .setRightEnds( + ebnfRights.containsKey(newNt.getName()) ? ebnfRights.get(newNt.getName()) : new HashSet<>()) + .setDefinition(symbol) + .build()); + + Builder copyBuilder = arguments == null ? newNt.copy() : newNt.copy().apply(arguments); + return copyBuilder.addConditions(symbol).setLabel(symbol.getLabel()).build(); + } + + /** + * S* ::= S+ + * | epsilon + * + */ + @Override + public Symbol visit(Star symbol) { + Symbol S = new Plus.Builder(symbol.getSymbol()).addSeparators(symbol.getSeparators()).build().accept(this); + + init(); + String[] parameters = null; + Expression[] arguments = null; + + S.setEmpty(); + visitor.visitSymbol(S); + + if (!freeVars.isEmpty()) { + freeVars.removeAll(state); + parameters = freeVars.toArray(new String[0]); + arguments = freeVars.stream().map(v -> AST.var(v)).toArray(Expression[]::new); + } + + String base = getName(symbol.getSymbol(), symbol.getSeparators(), layout); + Nonterminal newNt = parameters != null ? new Nonterminal.Builder(base + "*").addParameters(parameters) + .setNodeType(NonterminalNodeType.Star).build() + : new Nonterminal.Builder(base + "*").setNodeType(NonterminalNodeType.Star).build(); + + addedRules.add(RuntimeRule.withHead(newNt).addSymbols(S) + .setLayout(layout).setLayoutStrategy(strategy) + .setRecursion(Recursion.NON_REC).setAssociativity(Associativity.UNDEFINED) + .setPrecedence(-1).setPrecedenceLevel(PrecedenceLevel.getFirstAndDone()) + .setLeftEnd(S.getName()) + .setRightEnd(S.getName()) + .setLeftEnds(ebnfLefts.containsKey(newNt.getName()) ? ebnfLefts.get(newNt.getName()) : new HashSet<>()) + .setRightEnds( + ebnfRights.containsKey(newNt.getName()) ? ebnfRights.get(newNt.getName()) : new HashSet<>()) + .setDefinition(symbol) + .build()); + addedRules.add(RuntimeRule.withHead(newNt) + .setRecursion(Recursion.NON_REC).setAssociativity(Associativity.UNDEFINED) + .setPrecedence(-1).setPrecedenceLevel(PrecedenceLevel.getFirstAndDone()) + .setLeftEnds(ebnfLefts.containsKey(newNt.getName()) ? ebnfLefts.get(newNt.getName()) : new HashSet<>()) + .setRightEnds( + ebnfRights.containsKey(newNt.getName()) ? ebnfRights.get(newNt.getName()) : new HashSet<>()) + .setDefinition(symbol) + .build()); + + Builder copyBuilder = arguments == null ? newNt.copy() : newNt.copy().apply(arguments); + return copyBuilder.addConditions(symbol).setLabel(symbol.getLabel()).build(); + } + + @Override + public Symbol visit(Start start) { + return start; +// return start.getNonterminal().accept(this); + } + + /** + * S_IF(cond) ::= [ cond] S + * | [!cond] epsilon + */ + @Override + public Symbol visit(IfThen symbol) { + + Expression cond = symbol.getExpression(); + Symbol thenPart = symbol.getThenPart().accept(this); + + init(); + String[] parameters; + Expression[] arguments; + + thenPart.setEmpty(); + visitor.visitSymbol(thenPart); + + String id = "condIF"; + + if (!freeVars.isEmpty()) { + + freeVars.removeAll(state); + + parameters = new String[freeVars.size() + 1]; + arguments = new Expression[freeVars.size() + 1]; + + parameters[0] = id; + arguments[0] = cond; + + int i = 1; + for (String var : freeVars) { + parameters[i] = var; + arguments[i] = AST.var(var); + i++; + } + } else { + parameters = new String[] {id}; + arguments = new Expression[] {cond}; + } + + Nonterminal newNt = new Nonterminal.Builder("IF_" + counter++).addParameters(parameters).build(); + + addedRules.add(RuntimeRule.withHead(newNt).addSymbol( + thenPart.copy().addPreCondition(DataDependentCondition.predicate(AST.var(id))).build()) + .setLayout(layout).setLayoutStrategy(strategy) + .setRecursion(Recursion.NON_REC).setAssociativity(Associativity.UNDEFINED) + .setPrecedence(-1).setPrecedenceLevel(PrecedenceLevel.getFirstAndDone()) + .setDefinition(symbol) + .build()); + + // FIXME: epsilon rule can have a condition + addedRules.add(RuntimeRule.withHead(newNt) + .setRecursion(Recursion.NON_REC).setAssociativity(Associativity.UNDEFINED) + .setPrecedence(-1).setPrecedenceLevel(PrecedenceLevel.getFirstAndDone()).build()); + + return newNt.copy().apply(arguments).addConditions(symbol).setLabel(symbol.getLabel()).build(); + } + + /** + * AB_IF_ELSE(cond) ::= [ cond] A + * | [!cond] B + */ + @Override + public Symbol visit(IfThenElse symbol) { + + Expression cond = symbol.getExpression(); + Symbol thenPart = symbol.getThenPart().accept(this); + Symbol elsePart = symbol.getElsePart().accept(this); + + init(); + String[] parameters; + Expression[] arguments; + + thenPart.setEmpty(); + elsePart.setEmpty(); + visitor.visitSymbol(thenPart); + visitor.visitSymbol(elsePart); + + String id = "condIF"; + + if (!freeVars.isEmpty()) { + + freeVars.removeAll(state); + + parameters = new String[freeVars.size() + 1]; + arguments = new Expression[freeVars.size() + 1]; + + parameters[0] = id; + arguments[0] = cond; + + int i = 1; + for (String var : freeVars) { + parameters[i] = var; + arguments[i] = AST.var(var); + i++; + } + } else { + parameters = new String[] {id}; + arguments = new Expression[] {cond}; + } + + Nonterminal newNt = new Nonterminal.Builder("IF_THEN_ELSE_" + counter++).addParameters(parameters).build(); + + addedRules.add(RuntimeRule.withHead(newNt).addSymbol( + thenPart.copy().addPreCondition(DataDependentCondition.predicate(AST.var(id))).build()) + .setLayout(layout).setLayoutStrategy(strategy) + .setRecursion(Recursion.NON_REC).setAssociativity(Associativity.UNDEFINED) + .setPrecedence(-1).setPrecedenceLevel(PrecedenceLevel.getFirstAndDone()) + .setDefinition(symbol) + .build()); + + addedRules.add(RuntimeRule.withHead(newNt).addSymbol( + elsePart.copy().addPreCondition(DataDependentCondition.predicate(AST.not(AST.var(id)))).build()) + .setLayout(layout).setLayoutStrategy(strategy) + .setRecursion(Recursion.NON_REC).setAssociativity(Associativity.UNDEFINED) + .setPrecedence(-1).setPrecedenceLevel(PrecedenceLevel.getFirstAndDone()) + .setDefinition(symbol) + .build()); + + return newNt.copy().apply(arguments).addConditions(symbol).setLabel(symbol.getLabel()).build(); + } + + /** + * The rest: + */ + + @Override + public Symbol visit(Align symbol) { + Symbol sym = symbol.getSymbol().accept(this); + + return sym == symbol.getSymbol() ? symbol + : new Align.Builder(sym).setLabel(symbol.getLabel()).addConditions(symbol).build(); + } + + @Override + public Symbol visit(Block symbol) { + List symbols = symbol.getSymbols(); + Symbol[] syms = new Symbol[symbols.size()]; + + int j = 0; + boolean modified = false; + for (Symbol sym : symbols) { + syms[j] = sym.accept(this); + if (sym != syms[j]) + modified |= true; + j++; + } + return modified + ? new Block.Builder(syms).setLabel(symbol.getLabel()).addConditions(symbol).build() + : symbol; + } + + @Override + public Symbol visit(Code symbol) { + Symbol sym = symbol.getSymbol().accept(this); + if (sym == symbol.getSymbol()) + return symbol; + + return new Code.Builder(sym, symbol.getStatements()).setLabel(symbol.getLabel()).addConditions(symbol) + .build(); + } + + @Override + public Symbol visit(Error error) { + return error; + } + + @Override + public Symbol visit(Conditional symbol) { + Symbol sym = symbol.getSymbol().accept(this); + if (sym == symbol.getSymbol()) + return symbol; + + return new Conditional.Builder(sym, symbol.getExpression()).setLabel(symbol.getLabel()).addConditions( + symbol).build(); + } + + @Override + public Symbol visit(Ignore symbol) { + Symbol sym = symbol.getSymbol().accept(this); + + return sym == symbol.getSymbol() + ? symbol + : new Ignore.Builder(sym).setLabel(symbol.getLabel()).addConditions(symbol).build(); + } + + @Override + public Symbol visit(Nonterminal symbol) { + return symbol; + } + + @Override + public Symbol visit(Offside symbol) { + Symbol sym = symbol.getSymbol().accept(this); + + return sym == symbol.getSymbol() + ? symbol + : new Offside.Builder(sym).setLabel(symbol.getLabel()).addConditions(symbol).build(); + } + + @Override + public Symbol visit(Terminal symbol) { + return symbol; + } + + @Override + public Symbol visit(While symbol) { + Symbol body = symbol.getBody().accept(this); + if (body == symbol.getBody()) + return symbol; + + return new While.Builder(symbol.getExpression(), body).setLabel(symbol.getLabel()).addConditions(symbol) + .build(); + } + + @Override + public Symbol visit(Return symbol) { + return symbol; + } + + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/grammar/transformation/FindLabelsUsedInExcepts.java b/benchmarks/src/main/kotlin/org/iguana/grammar/transformation/FindLabelsUsedInExcepts.java new file mode 100644 index 000000000..05aa43479 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/grammar/transformation/FindLabelsUsedInExcepts.java @@ -0,0 +1,167 @@ +package org.iguana.grammar.transformation; + +import org.iguana.grammar.runtime.RuntimeGrammar; +import org.iguana.grammar.runtime.RuntimeRule; +import org.iguana.grammar.symbol.Align; +import org.iguana.grammar.symbol.Alt; +import org.iguana.grammar.symbol.Block; +import org.iguana.grammar.symbol.Code; +import org.iguana.grammar.symbol.Conditional; +import org.iguana.grammar.symbol.Error; +import org.iguana.grammar.symbol.Group; +import org.iguana.grammar.symbol.IfThen; +import org.iguana.grammar.symbol.IfThenElse; +import org.iguana.grammar.symbol.Ignore; +import org.iguana.grammar.symbol.Nonterminal; +import org.iguana.grammar.symbol.Offside; +import org.iguana.grammar.symbol.Opt; +import org.iguana.grammar.symbol.Plus; +import org.iguana.grammar.symbol.Return; +import org.iguana.grammar.symbol.Star; +import org.iguana.grammar.symbol.Start; +import org.iguana.grammar.symbol.Symbol; +import org.iguana.grammar.symbol.Terminal; +import org.iguana.grammar.symbol.While; +import org.iguana.traversal.ISymbolVisitor; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +public class FindLabelsUsedInExcepts implements ISymbolVisitor { + + private final Map> labels = new HashMap<>(); + + public Map> getLables() { + return labels; + } + + public void compute(RuntimeGrammar grammar) { + for (RuntimeRule rule : grammar.getRules()) + compute(rule); + } + + public void compute(RuntimeRule rule) { + + if (rule.getBody() == null) + return; + + for (Symbol symbol : rule.getBody()) + symbol.accept(this); + } + + @Override + public Void visit(Align symbol) { + symbol.getSymbol().accept(this); + return null; + } + + @Override + // Currently, also expected to be desugared as part of data-dependent EBNF constructs + public Void visit(Block symbol) { + throw new RuntimeException("Unexpected symbol: " + symbol); + } + + @Override + public Void visit(Code symbol) { + symbol.getSymbol().accept(this); + return null; + } + + @Override + public Void visit(Error error) { + return null; + } + + @Override + public Void visit(Conditional symbol) { + symbol.getSymbol().accept(this); + return null; + } + + @Override + // Currently, also expected to be desugared as part of data-dependent EBNF constructs + public Void visit(IfThen symbol) { + throw new RuntimeException("Unexpected symbol: " + symbol); + } + + @Override + // Currently, also expected to be desugared as part of data-dependent EBNF constructs + public Void visit(IfThenElse symbol) { + throw new RuntimeException("Unexpected symbol: " + symbol); + } + + @Override + public Void visit(Ignore symbol) { + symbol.getSymbol().accept(this); + return null; + } + + @Override + public Void visit(Nonterminal symbol) { + Set excepts = symbol.getExcepts(); + if (excepts != null && !excepts.isEmpty()) { + Set ls = labels.get(symbol.getName()); + if (ls == null) { + ls = new HashSet<>(); + labels.put(symbol.getName(), ls); + } + ls.addAll(excepts); + } + return null; + } + + @Override + public Void visit(Offside symbol) { + symbol.getSymbol().accept(this); + return null; + } + + @Override + public Void visit(Terminal symbol) { + return null; + } + + @Override + // Currently, also expected to be desugared as part of data-dependent EBNF constructs + public Void visit(While symbol) { + throw new RuntimeException("Unexpected symbol: " + symbol); + } + + @Override + public Void visit(Return symbol) { + return null; + } + + @Override + public Void visit(Alt symbol) { + throw new RuntimeException("Unexpected symbol: " + symbol); + } + + @Override + public Void visit(Opt symbol) { + throw new RuntimeException("Unexpected symbol: " + symbol); + } + + @Override + public Void visit(Plus symbol) { + throw new RuntimeException("Unexpected symbol: " + symbol); + } + + @Override + public Void visit(Group symbol) { + throw new RuntimeException("Unexpected symbol: " + symbol); + } + + @Override + public Void visit(Star symbol) { + throw new RuntimeException("Unexpected symbol: " + symbol); + } + + @Override + public Void visit(Start symbol) { + throw new RuntimeException("Unexpected symbol: " + symbol); + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/grammar/transformation/GrammarTransformation.java b/benchmarks/src/main/kotlin/org/iguana/grammar/transformation/GrammarTransformation.java new file mode 100644 index 000000000..6b9cecc22 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/grammar/transformation/GrammarTransformation.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.grammar.transformation; + +import org.iguana.grammar.runtime.RuntimeGrammar; + +@FunctionalInterface +public interface GrammarTransformation { + RuntimeGrammar transform(RuntimeGrammar grammar); +} diff --git a/benchmarks/src/main/kotlin/org/iguana/grammar/transformation/GrammarTransformer.java b/benchmarks/src/main/kotlin/org/iguana/grammar/transformation/GrammarTransformer.java new file mode 100644 index 000000000..a90b950c7 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/grammar/transformation/GrammarTransformer.java @@ -0,0 +1,24 @@ +package org.iguana.grammar.transformation; + +import org.iguana.grammar.runtime.RuntimeGrammar; + +public class GrammarTransformer { + + public static RuntimeGrammar transform(RuntimeGrammar runtimeGrammar) { + DesugarAlignAndOffside desugarAlignAndOffside = new DesugarAlignAndOffside(); + desugarAlignAndOffside.doAlign(); + RuntimeGrammar grammar = desugarAlignAndOffside.transform(runtimeGrammar); + grammar = new EBNFToBNF().transform(grammar); + desugarAlignAndOffside.doOffside(); + grammar = desugarAlignAndOffside.transform(grammar); + grammar = new DesugarStartSymbol(runtimeGrammar.getStartSymbols()).transform(grammar); + DesugarPrecedenceAndAssociativity precedenceAndAssociativity = new DesugarPrecedenceAndAssociativity(); + precedenceAndAssociativity.setOP2(); + grammar = precedenceAndAssociativity.transform(grammar); + grammar = new DesugarState().transform(grammar); + grammar = new LayoutWeaver().transform(grammar); + return grammar; + } +} + + diff --git a/benchmarks/src/main/kotlin/org/iguana/grammar/transformation/GrammarVisitor.java b/benchmarks/src/main/kotlin/org/iguana/grammar/transformation/GrammarVisitor.java new file mode 100644 index 000000000..d358e6263 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/grammar/transformation/GrammarVisitor.java @@ -0,0 +1,57 @@ +package org.iguana.grammar.transformation; + +import org.iguana.grammar.symbol.Alternative; +import org.iguana.grammar.symbol.PriorityLevel; +import org.iguana.grammar.symbol.Rule; +import org.iguana.grammar.symbol.Sequence; +import org.iguana.grammar.symbol.Symbol; +import org.iguana.traversal.SymbolToSymbolVisitor; + +import java.util.ArrayList; +import java.util.List; + +public class GrammarVisitor { + + private final SymbolToSymbolVisitor symbolToSymbolVisitor; + + public GrammarVisitor(SymbolToSymbolVisitor symbolToSymbolVisitor) { + this.symbolToSymbolVisitor = symbolToSymbolVisitor; + } + + public List transform(List rules) { + List result = new ArrayList<>(); + for (Rule rule : rules) { + result.add(get(rule, transform(rule))); + } + return result; + } + + private Rule transform(Rule rule) { + Rule.Builder ruleBuilder = rule.copy(); + ruleBuilder.clearPriorityLevels(); + for (PriorityLevel priorityLevel : rule.getPriorityLevels()) { + PriorityLevel.Builder priorityLevelBuilder = new PriorityLevel.Builder(); + for (Alternative alternative : priorityLevel.getAlternatives()) { + Alternative.Builder alternativeBuilder = alternative.copy(); + alternativeBuilder.clearSequences(); + for (Sequence sequence : alternative.seqs()) { + Sequence.Builder sequenceBuilder = sequence.copy(); + sequenceBuilder.clearSymbols(); + for (Symbol symbol : sequence.getSymbols()) { + Symbol newSymbol = symbol.accept(symbolToSymbolVisitor); + sequenceBuilder.addSymbol(get(symbol, newSymbol)); + } + alternativeBuilder.addSequence(get(sequence, sequenceBuilder.build())); + } + priorityLevelBuilder.addAlternative(get(alternative, alternativeBuilder.build())); + } + ruleBuilder.addPriorityLevel(get(priorityLevel, priorityLevelBuilder.build())); + } + return ruleBuilder.build(); + } + + private T get(T original, T copy) { + if (original.equals(copy)) return original; + return copy; + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/grammar/transformation/LayoutWeaver.java b/benchmarks/src/main/kotlin/org/iguana/grammar/transformation/LayoutWeaver.java new file mode 100644 index 000000000..f656b3376 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/grammar/transformation/LayoutWeaver.java @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.grammar.transformation; + +import org.iguana.grammar.condition.Condition; +import org.iguana.grammar.condition.ConditionType; +import org.iguana.grammar.runtime.RuntimeGrammar; +import org.iguana.grammar.runtime.RuntimeRule; +import org.iguana.grammar.slot.NonterminalNodeType; +import org.iguana.grammar.symbol.Error; +import org.iguana.grammar.symbol.Return; +import org.iguana.grammar.symbol.Symbol; + +import java.util.LinkedHashSet; +import java.util.Set; + +import static org.iguana.grammar.symbol.LayoutStrategy.INHERITED; + +public class LayoutWeaver implements GrammarTransformation { + + @Override + public RuntimeGrammar transform(RuntimeGrammar grammar) { + Symbol layout = grammar.getLayout(); + + RuntimeGrammar.Builder builder = RuntimeGrammar.builder().setLayout(layout).setStartSymbols( + grammar.getStartSymbols()); + + for (RuntimeRule rule : grammar.getRules()) { + RuntimeRule.Builder ruleBuilder = RuntimeRule.withHead(rule.getHead()) + .setRecursion(rule.getRecursion()) + .setAssociativity(rule.getAssociativity()) + .setAssociativityGroup(rule.getAssociativityGroup()) + .setPrecedence(rule.getPrecedence()) + .setPrecedenceLevel(rule.getPrecedenceLevel()) + .setLabel(rule.getLabel()) + .setAttributes(rule.getAttributes()) + .setDefinition(rule.getDefinition()); + + if (rule.size() == 0) { + builder.addRule(ruleBuilder.build()); + continue; + } + + if (rule.getHead().getNodeType() == NonterminalNodeType.Start) { + if (layout == null) { + builder.addRule(ruleBuilder.addSymbol(rule.symbolAt(0)).build()); + } else { + builder.addRule(ruleBuilder.addSymbol(layout).addSymbol(rule.symbolAt(0)).addSymbol(layout) + .build()); + } + continue; + } + + for (int i = 0; i < rule.size() - 1; i++) { + Symbol s = rule.symbolAt(i); + + Set ignoreLayoutConditions = getIgnoreLayoutConditions(s); + + if (i == rule.size() - 2 && rule.symbolAt(rule.size() - 1) instanceof Return + && ignoreLayoutConditions.isEmpty()) { + ruleBuilder.addSymbol(s); + continue; + } + + if (ignoreLayoutConditions.isEmpty()) + ruleBuilder.addSymbol(s); + else + ruleBuilder.addSymbol(s.copy().removePostConditions(ignoreLayoutConditions).build()); + + // Do not insert layout after the layout symbol because we rely on the first set of the next + // non-layout symbol for synchronization. + if (!(s instanceof Error)) { + addLayout(layout, rule, ruleBuilder, s); + } + } + + Symbol last = rule.symbolAt(rule.size() - 1); + Set ignoreLayoutConditions = getIgnoreLayoutConditions(last); + + if (ignoreLayoutConditions.isEmpty()) + ruleBuilder.addSymbol(last); + else + ruleBuilder.addSymbol(last.copy().removePostConditions(ignoreLayoutConditions).build()); + + if (!ignoreLayoutConditions.isEmpty()) { + addLayout(layout, rule, ruleBuilder, last); + } + + if (rule.getLayoutStrategy() == INHERITED) { + ruleBuilder.setLayout(layout); + } + builder.addRule(ruleBuilder.build()); + } + + builder.setGlobals(grammar.getGlobals()); + builder.setEbnfLefts(grammar.getEBNFLefts()); + builder.setEbnfRights(grammar.getEBNFRights()); + builder.setRegularExpressionDefinitions(grammar.getRegularExpressionDefinitions()); + return builder.build(); + } + + private void addLayout(Symbol layout, RuntimeRule rule, RuntimeRule.Builder ruleBuilder, Symbol s) { + switch (rule.getLayoutStrategy()) { + case NO_LAYOUT: + // do nothing + break; + + case INHERITED: + if (layout != null) + ruleBuilder.addSymbol(layout.copy().addPostConditions(getIgnoreLayoutConditions(s)).build()); + break; + + case FIXED: + ruleBuilder.addSymbol(rule.getLayout().copy().addPostConditions(getIgnoreLayoutConditions(s)).build()); + break; + } + } + + private Set getIgnoreLayoutConditions(Symbol s) { + Set conditions = new LinkedHashSet<>(); + for (Condition c : s.getPostConditions()) { + if (c.getType() == ConditionType.NOT_FOLLOW_IGNORE_LAYOUT + || c.getType() == ConditionType.FOLLOW_IGNORE_LAYOUT) { + conditions.add(c); + } + } + return conditions; + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/grammar/transformation/ResolveIdentifiers.java b/benchmarks/src/main/kotlin/org/iguana/grammar/transformation/ResolveIdentifiers.java new file mode 100644 index 000000000..0a145a31f --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/grammar/transformation/ResolveIdentifiers.java @@ -0,0 +1,56 @@ +package org.iguana.grammar.transformation; + +import org.iguana.grammar.slot.TerminalNodeType; +import org.iguana.grammar.symbol.Identifier; +import org.iguana.grammar.symbol.Nonterminal; +import org.iguana.grammar.symbol.Symbol; +import org.iguana.grammar.symbol.Terminal; +import org.iguana.regex.Reference; +import org.iguana.regex.RegularExpression; +import org.iguana.traversal.SymbolToSymbolVisitor; + +import java.util.Map; +import java.util.Set; + +public class ResolveIdentifiers implements SymbolToSymbolVisitor { + + private final Set nonterminals; + private final Map regularExpressions; + + public ResolveIdentifiers(Set nonterminals, Map regularExpressions) { + this.nonterminals = nonterminals; + this.regularExpressions = regularExpressions; + } + + @Override + public Symbol visit(Identifier id) { + if (nonterminals.contains(id.getName())) { + return new Nonterminal.Builder(id.getName()) + .addPreConditions(visitPreConditions(id)) + .addPostConditions(visitPostConditions(id)) + .addExcepts(id.getExcepts()) + .setLabel(id.getLabel()) + .build(); + } else if (regularExpressions.containsKey(id.getName())) { + RegularExpression regularExpression = regularExpressions.get(id.getName()); + return new Terminal.Builder(regularExpression) + .setNodeType(TerminalNodeType.Regex) + .setPreConditions(visitPreConditions(id)) + .setPostConditions(visitPostConditions(id)) + .setLabel(id.getLabel()) + .setName(id.getName()) + .build(); + } else { + throw new RuntimeException("Identifier '" + id + "' is not defined."); + } + } + + @Override + public RegularExpression visit(Reference ref) { + if (regularExpressions.containsKey(ref.getName())) { + return regularExpressions.get(ref.getName()); + } else { + throw new RuntimeException("Terminal '" + ref.getName() + "' is not defined."); + } + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/grammar/transformation/SymbolTransformation.java b/benchmarks/src/main/kotlin/org/iguana/grammar/transformation/SymbolTransformation.java new file mode 100644 index 000000000..f7c67dd2f --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/grammar/transformation/SymbolTransformation.java @@ -0,0 +1,25 @@ +package org.iguana.grammar.transformation; + +import org.iguana.grammar.symbol.Symbol; + +import java.util.function.Function; +import java.util.function.Predicate; + +public class SymbolTransformation { + + private final Predicate predicate; + private final Function transformer; + + public SymbolTransformation(Predicate predicate, Function transformer) { + this.predicate = predicate; + this.transformer = transformer; + } + + public boolean isDefinedAt(Symbol symbol) { + return predicate.test(symbol); + } + + public Symbol apply(Symbol symbol) { + return transformer.apply(symbol); + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/grammar/transformation/TopLevelPrecedenceNonterminals.java b/benchmarks/src/main/kotlin/org/iguana/grammar/transformation/TopLevelPrecedenceNonterminals.java new file mode 100644 index 000000000..ce9097cd9 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/grammar/transformation/TopLevelPrecedenceNonterminals.java @@ -0,0 +1,44 @@ +package org.iguana.grammar.transformation; + +import org.iguana.grammar.symbol.Alternative; +import org.iguana.grammar.symbol.Associativity; +import org.iguana.grammar.symbol.PriorityLevel; +import org.iguana.grammar.symbol.Rule; +import org.iguana.grammar.symbol.Sequence; + +import java.util.ArrayList; +import java.util.List; + +public class TopLevelPrecedenceNonterminals { + + public static List addTopLevelPrecedenceRules(List rules) { + List newRules = new ArrayList<>(); + for (Rule rule : rules) { + if (isPrecedenceRule(rule)) { + Rule newRule = new Rule.Builder(rule.getHead().copy().setName("$_" + rule.getHead().getName()).build()) + .addPriorityLevel(PriorityLevel.from(Alternative.from(Sequence.from( + rule.getHead().copy().setLabel("child").build() + )))).build(); + newRules.add(newRule); + } + } + + return newRules; + } + + /** + * A rule is a precedence rule if one of these conditions is true: + * - has more than one priority level + * - has one priority level and there is associativity defined for the alternative or individual sequences + */ + private static boolean isPrecedenceRule(Rule rule) { + int numPriorityLevels = rule.getPriorityLevels().size(); + if (numPriorityLevels == 0) return false; + if (numPriorityLevels > 1) return true; + List alternatives = rule.getPriorityLevels().get(0).getAlternatives(); + if (alternatives.stream().anyMatch(alternative -> alternative.getAssociativity() != Associativity.UNDEFINED)) + return true; + return alternatives.stream().flatMap(alternative -> alternative.seqs().stream()) + .anyMatch(sequence -> sequence.getAssociativity() != Associativity.UNDEFINED); + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/grammar/transformation/VarToInt.java b/benchmarks/src/main/kotlin/org/iguana/grammar/transformation/VarToInt.java new file mode 100644 index 000000000..4a0df64e5 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/grammar/transformation/VarToInt.java @@ -0,0 +1,597 @@ +package org.iguana.grammar.transformation; + +import org.iguana.datadependent.ast.AST; +import org.iguana.datadependent.ast.AbstractAST; +import org.iguana.datadependent.ast.Expression.Add; +import org.iguana.datadependent.ast.Expression.And; +import org.iguana.datadependent.ast.Expression.AndIndent; +import org.iguana.datadependent.ast.Expression.Assignment; +import org.iguana.datadependent.ast.Expression.Boolean; +import org.iguana.datadependent.ast.Expression.Call; +import org.iguana.datadependent.ast.Expression.Divide; +import org.iguana.datadependent.ast.Expression.EndOfFile; +import org.iguana.datadependent.ast.Expression.Equal; +import org.iguana.datadependent.ast.Expression.Greater; +import org.iguana.datadependent.ast.Expression.GreaterThanEqual; +import org.iguana.datadependent.ast.Expression.Integer; +import org.iguana.datadependent.ast.Expression.LShiftANDEqZero; +import org.iguana.datadependent.ast.Expression.LeftExtent; +import org.iguana.datadependent.ast.Expression.Less; +import org.iguana.datadependent.ast.Expression.LessThanEqual; +import org.iguana.datadependent.ast.Expression.Multiply; +import org.iguana.datadependent.ast.Expression.Name; +import org.iguana.datadependent.ast.Expression.Not; +import org.iguana.datadependent.ast.Expression.NotEqual; +import org.iguana.datadependent.ast.Expression.Or; +import org.iguana.datadependent.ast.Expression.OrIndent; +import org.iguana.datadependent.ast.Expression.Real; +import org.iguana.datadependent.ast.Expression.RightExtent; +import org.iguana.datadependent.ast.Expression.String; +import org.iguana.datadependent.ast.Expression.Subtract; +import org.iguana.datadependent.ast.Expression.Tuple; +import org.iguana.datadependent.ast.Expression.Val; +import org.iguana.datadependent.ast.Expression.Yield; +import org.iguana.datadependent.ast.Statement; +import org.iguana.datadependent.ast.Statement.Expression; +import org.iguana.datadependent.ast.VariableDeclaration; +import org.iguana.datadependent.traversal.IAbstractASTVisitor; +import org.iguana.grammar.condition.Condition; +import org.iguana.grammar.condition.DataDependentCondition; +import org.iguana.grammar.condition.PositionalCondition; +import org.iguana.grammar.condition.RegularExpressionCondition; +import org.iguana.grammar.exception.UndeclaredVariableException; +import org.iguana.grammar.runtime.RuntimeGrammar; +import org.iguana.grammar.runtime.RuntimeRule; +import org.iguana.grammar.symbol.Align; +import org.iguana.grammar.symbol.Alt; +import org.iguana.grammar.symbol.Block; +import org.iguana.grammar.symbol.Code; +import org.iguana.grammar.symbol.Conditional; +import org.iguana.grammar.symbol.Error; +import org.iguana.grammar.symbol.Group; +import org.iguana.grammar.symbol.IfThen; +import org.iguana.grammar.symbol.IfThenElse; +import org.iguana.grammar.symbol.Ignore; +import org.iguana.grammar.symbol.Nonterminal; +import org.iguana.grammar.symbol.Offside; +import org.iguana.grammar.symbol.Opt; +import org.iguana.grammar.symbol.Plus; +import org.iguana.grammar.symbol.Return; +import org.iguana.grammar.symbol.Star; +import org.iguana.grammar.symbol.Start; +import org.iguana.grammar.symbol.Symbol; +import org.iguana.grammar.symbol.Terminal; +import org.iguana.grammar.symbol.While; +import org.iguana.traversal.IConditionVisitor; +import org.iguana.traversal.ISymbolVisitor; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public class VarToInt implements GrammarTransformation, IAbstractASTVisitor, ISymbolVisitor, + IConditionVisitor { + + private Map> mapping; + + private Map current; + + public Map> getMapping() { + return mapping; + } + + @Override + public RuntimeGrammar transform(RuntimeGrammar grammar) { + mapping = new HashMap<>(); + Set rules = new LinkedHashSet<>(); + int i = 0; + for (RuntimeRule rule : grammar.getRules()) { + rules.add(transform(rule)); + mapping.put(i, current); + i++; + } + return RuntimeGrammar.builder() + .addRules(rules) + .addEBNFl(grammar.getEBNFLefts()) + .addEBNFr(grammar.getEBNFRights()) + .setLayout(grammar.getLayout()) + .build(); + } + + public RuntimeRule transform(RuntimeRule rule) { + current = new HashMap<>(); + + List parameters = rule.getHead().getParameters(); + + if (parameters != null) { + for (java.lang.String parameter : parameters) + current.put(parameter, current.size()); + } + + List body = new ArrayList<>(); + + for (Symbol symbol : rule.getBody()) + body.add(visit(symbol)); + + return RuntimeRule.withHead(rule.getHead()) + .addSymbols(body) + .setRecursion(rule.getRecursion()) + .setAssociativity(rule.getAssociativity()) + .setPrecedence(rule.getPrecedence()) + .setPrecedenceLevel(rule.getPrecedenceLevel()) + .setiRecursion(rule.getIRecursion()) + .setLeftEnd(rule.getLeftEnd()) + .setRightEnd(rule.getRightEnd()) + .setLeftEnds(rule.getLeftEnds()) + .setRightEnds(rule.getRightEnds()) + .setLayout(rule.getLayout()) + .setLayoutStrategy(rule.getLayoutStrategy()) + .setLabel(rule.getLabel()) + .setDefinition(rule.getDefinition()) + .build(); + } + + private Symbol visit(Symbol symbol) { + java.lang.String label = symbol.getLabel(); + + if (label != null && !label.isEmpty()) + current.put(label, current.size()); + + Symbol sym = symbol.accept(this); + + Set preConditions = new LinkedHashSet<>(); + Set postConditions = new LinkedHashSet<>(); + + for (Condition condition : symbol.getPreConditions()) + preConditions.add(condition.accept(this)); + + for (Condition condition : symbol.getPostConditions()) + postConditions.add(condition.accept(this)); + + return sym.copy() + .setLabel(symbol.getLabel()) + .addPreConditions(preConditions) + .addPostConditions(postConditions) + .build(); + } + + @Override + public Symbol visit(Align symbol) { + return Align.align(visit(symbol.getSymbol())); + } + + @Override + public Symbol visit(Block symbol) { + throw new RuntimeException("Unsupported symbol: var-to-int!"); + } + + + @Override + public Symbol visit(Code symbol) { + Symbol sym = visit(symbol.getSymbol()); + + Statement[] statements = new Statement[symbol.getStatements().length]; + int i = 0; + for (Statement statement : symbol.getStatements()) + statements[i++] = (Statement) statement.accept(this); + + return Code.code(sym, statements); + } + + @Override + public Symbol visit(Error error) { + return error; + } + + @Override + public Symbol visit(Conditional symbol) { + Symbol sym = visit(symbol.getSymbol()); + return Conditional.when(sym, (org.iguana.datadependent.ast.Expression) symbol.getExpression().accept(this)); + } + + @Override + public Symbol visit(IfThen symbol) { + throw new RuntimeException("Unsupported symbol: var-to-int!"); + } + + @Override + public Symbol visit(IfThenElse symbol) { + throw new RuntimeException("Unsupported symbol: var-to-int!"); + } + + @Override + public Symbol visit(Ignore symbol) { + return Ignore.ignore(visit(symbol.getSymbol())); + } + + @Override + public Symbol visit(Nonterminal symbol) { + java.lang.String variable = symbol.getVariable(); + + if (variable != null && !variable.isEmpty()) + current.put(variable, current.size()); + + if (symbol.getArguments() == null || symbol.getArguments().length == 0) + return new Nonterminal.Builder(symbol.getName()) + .setIndex(symbol.getIndex()) + .setNodeType(symbol.getNodeType()) + .setVariable(symbol.getVariable()) + .addExcepts(symbol.getExcepts()) + .build(); + + org.iguana.datadependent.ast.Expression[] arguments = + new org.iguana.datadependent.ast.Expression[symbol.getArguments().length]; + + int i = 0; + for (org.iguana.datadependent.ast.Expression e : symbol.getArguments()) + arguments[i++] = (org.iguana.datadependent.ast.Expression) e.accept(this); + + return new Nonterminal.Builder(symbol.getName()) + .setIndex(symbol.getIndex()) + .apply(arguments) + .setVariable(symbol.getVariable()) + .addExcepts(symbol.getExcepts()) + .build(); + } + + @Override + public Symbol visit(Offside symbol) { + return Offside.offside(visit(symbol.getSymbol())); + } + + @Override + public Symbol visit(Terminal symbol) { + return new Terminal.Builder(symbol.getRegularExpression()) + .setNodeType(symbol.getNodeType()) + .setPreConditions(symbol.getPreConditions()) + .setPostConditions(symbol.getPostConditions()) + .setName(symbol.getName()) + .build(); + } + + @Override + public Symbol visit(While symbol) { + throw new RuntimeException("Unsupported symbol: var-to-int!"); + } + + @Override + public Symbol visit(Return symbol) { + return Return.ret((org.iguana.datadependent.ast.Expression) symbol.getExpression().accept(this)); + } + + @Override + public Symbol visit(Alt symbol) { + throw new RuntimeException("Unsupported symbol: var-to-int!"); + } + + @Override + public Symbol visit(Opt symbol) { + throw new RuntimeException("Unsupported symbol: var-to-int!"); + } + + @Override + public Symbol visit(Plus symbol) { + throw new RuntimeException("Unsupported symbol: var-to-int!"); + } + + @Override + public Symbol visit(Group symbol) { + throw new RuntimeException("Unsupported symbol: var-to-int!"); + } + + @Override + public Symbol visit(Star symbol) { + throw new RuntimeException("Unsupported symbol: var-to-int!"); + } + + @Override + public Symbol visit(Start start) { + throw new RuntimeException("Unsupported symbol: var-to-int!"); + } + + @Override + public AbstractAST visit(Boolean expression) { + return expression; + } + + @Override + public AbstractAST visit(Integer expression) { + return expression; + } + + @Override + public AbstractAST visit(Real expression) { + return expression; + } + + @Override + public AbstractAST visit(String expression) { + return expression; + } + + @Override + public AbstractAST visit(Not not) { + return AST.not((org.iguana.datadependent.ast.Expression) not.getExp().accept(this)); + } + + @Override + public AbstractAST visit(Tuple expression) { + org.iguana.datadependent.ast.Expression[] expressions = + new org.iguana.datadependent.ast.Expression[expression.getElements().length]; + + int i = 0; + for (org.iguana.datadependent.ast.Expression e : expression.getElements()) + expressions[i++] = (org.iguana.datadependent.ast.Expression) e.accept(this); + + if (expressions.length == 2 && expressions[0] instanceof Integer && expressions[1] instanceof Integer) { + return AST.intTuple2((Integer) expressions[0], (Integer) expressions[1]); + } + + return AST.tuple(expressions); + } + + @Override + public AbstractAST visit(Name expression) { + java.lang.String name = expression.getName(); + java.lang.Integer i = current.get(name); + + if (i == null) + throw new UndeclaredVariableException(name); + + return AST.var(name, i); + } + + @Override + public AbstractAST visit(Call expression) { + org.iguana.datadependent.ast.Expression[] arguments = + new org.iguana.datadependent.ast.Expression[expression.getArguments().length]; + int i = 0; + for (org.iguana.datadependent.ast.Expression e : expression.getArguments()) + arguments[i++] = (org.iguana.datadependent.ast.Expression) e.accept(this); + + java.lang.String name = expression.getFunName(); + + switch (name) { + case "println": + return AST.println(arguments); + case "indent": + return AST.indent(arguments[0]); + case "ppDeclare": + return AST.ppDeclare(arguments[0], arguments[1]); + case "ppLookup": + return AST.ppLookup(arguments[0]); + case "endsWith": + return AST.endsWith(arguments[0], arguments[1]); + case "startsWith": + return AST.startsWith(arguments[0]); + case "not": + return AST.not(arguments[0]); + case "neg": + return AST.neg(arguments[0]); + case "len": + return AST.len(arguments[0]); + case "pr1": + return AST.pr1(arguments[0], arguments[1], arguments[2]); + case "pr2": + org.iguana.datadependent.ast.Expression[] rest = + new org.iguana.datadependent.ast.Expression[arguments.length - 2]; + System.arraycopy(arguments, 2, rest, 0, arguments.length - 2); + return AST.pr2(arguments[0], arguments[1], rest); + case "pr3": + return AST.pr3(arguments[0], arguments[1]); + case "min": + return AST.min(arguments[0], arguments[1]); + case "map": + return AST.map(); + case "put": + if (arguments.length == 2) + return AST.put(arguments[0], arguments[1]); + else + return AST.put(arguments[0], arguments[1], arguments[3]); + case "contains": + return AST.contains(arguments[0], arguments[1]); + case "push": + return AST.push(arguments[0], arguments[1]); + case "pop": + return AST.pop(arguments[0]); + case "top": + return AST.top(arguments[0]); + case "find": + return AST.find(arguments[0], arguments[1]); + case "get": + if (arguments[1] instanceof Integer) { + return AST.get(arguments[0], ((Integer) arguments[1]).getValue()); + } + return AST.get(arguments[0], arguments[1]); + case "undef": + return AST.undef(); + case "shift": + return AST.shift(arguments[0], arguments[1]); + default: + throw new UndeclaredVariableException(name); + } + } + + @Override + public AbstractAST visit(Assignment expression) { + + java.lang.String id = expression.getId(); + java.lang.Integer i = current.get(id); + + if (i == null) + throw new UndeclaredVariableException(id); + + return AST.assign(id, i, (org.iguana.datadependent.ast.Expression) expression.getExpression().accept(this)); + } + + @Override + public AbstractAST visit(LShiftANDEqZero expression) { + return AST.lShiftANDEqZero((org.iguana.datadependent.ast.Expression) expression.getLhs().accept(this), + (org.iguana.datadependent.ast.Expression) expression.getRhs().accept(this)); + } + + @Override + public AbstractAST visit(OrIndent expression) { + return AST.orIndent((org.iguana.datadependent.ast.Expression) expression.getIndex().accept(this), + (org.iguana.datadependent.ast.Expression) expression.getIndent().accept(this), + (org.iguana.datadependent.ast.Expression) expression.getFirst().accept(this), + (org.iguana.datadependent.ast.Expression) expression.getLExt().accept(this)); + } + + @Override + public AbstractAST visit(AndIndent expression) { + return AST.andIndent((org.iguana.datadependent.ast.Expression) expression.getIndex().accept(this), + (org.iguana.datadependent.ast.Expression) expression.getFirst().accept(this), + (org.iguana.datadependent.ast.Expression) expression.getLExt().accept(this)); + } + + @Override + public AbstractAST visit(Or expression) { + return AST.or((org.iguana.datadependent.ast.Expression) expression.getLhs().accept(this), + (org.iguana.datadependent.ast.Expression) expression.getRhs().accept(this)); + } + + @Override + public AbstractAST visit(And expression) { + return AST.and((org.iguana.datadependent.ast.Expression) expression.getLhs().accept(this), + (org.iguana.datadependent.ast.Expression) expression.getRhs().accept(this)); + } + + @Override + public AbstractAST visit(Less expression) { + return AST.less((org.iguana.datadependent.ast.Expression) expression.getLhs().accept(this), + (org.iguana.datadependent.ast.Expression) expression.getRhs().accept(this)); + } + + @Override + public AbstractAST visit(LessThanEqual expression) { + return AST.lessEq((org.iguana.datadependent.ast.Expression) expression.getLhs().accept(this), + (org.iguana.datadependent.ast.Expression) expression.getRhs().accept(this)); + } + + @Override + public AbstractAST visit(Greater expression) { + return AST.greater((org.iguana.datadependent.ast.Expression) expression.getLhs().accept(this), + (org.iguana.datadependent.ast.Expression) expression.getRhs().accept(this)); + } + + @Override + public AbstractAST visit(GreaterThanEqual expression) { + return AST.greaterEq((org.iguana.datadependent.ast.Expression) expression.getLhs().accept(this), + (org.iguana.datadependent.ast.Expression) expression.getRhs().accept(this)); + } + + @Override + public AbstractAST visit(Equal expression) { + return AST.equal((org.iguana.datadependent.ast.Expression) expression.getLhs().accept(this), + (org.iguana.datadependent.ast.Expression) expression.getRhs().accept(this)); + } + + @Override + public AbstractAST visit(NotEqual expression) { + return AST.notEqual((org.iguana.datadependent.ast.Expression) expression.getLhs().accept(this), + (org.iguana.datadependent.ast.Expression) expression.getRhs().accept(this)); + } + + @Override + public AbstractAST visit(LeftExtent expression) { + throw new RuntimeException("Unsupported yet expression: " + expression); + } + + @Override + public AbstractAST visit(RightExtent expression) { + throw new RuntimeException("Unsupported yet expression: " + expression); + } + + @Override + public AbstractAST visit(Yield expression) { + java.lang.String label = expression.getLabel(); + java.lang.Integer i = current.get(label); + + if (i == null) + throw new UndeclaredVariableException(label); + + return AST.yield(label, i); + } + + @Override + public AbstractAST visit(Val expression) { + throw new RuntimeException("Unsupported yet expression: " + expression); + } + + @Override + public AbstractAST visit(EndOfFile expression) { + return expression; + } + + @Override + public AbstractAST visit(org.iguana.datadependent.ast.Expression.IfThenElse expression) { + return AST.ifThenElse((org.iguana.datadependent.ast.Expression) expression.getCondition().accept(this), + (org.iguana.datadependent.ast.Expression) expression.getThenPart().accept(this), + (org.iguana.datadependent.ast.Expression) expression.getElsePart().accept(this)); + } + + @Override + public AbstractAST visit(VariableDeclaration declaration) { + int i = current.size(); + current.put(declaration.getName(), i); + return AST.varDecl(declaration.getName(), i, + (org.iguana.datadependent.ast.Expression) declaration.getExpression().accept(this)); + } + + @Override + public AbstractAST visit(org.iguana.datadependent.ast.Statement.VariableDeclaration declaration) { + return AST.varDeclStat((VariableDeclaration) declaration.getDeclaration().accept(this)); + } + + @Override + public AbstractAST visit(Add expression) { + return AST.and((org.iguana.datadependent.ast.Expression) expression.getLhs().accept(this), + (org.iguana.datadependent.ast.Expression) expression.getRhs().accept(this)); + } + + @Override + public AbstractAST visit(Subtract expression) { + return AST.subtract((org.iguana.datadependent.ast.Expression) expression.getLhs().accept(this), + (org.iguana.datadependent.ast.Expression) expression.getRhs().accept(this)); + + } + + @Override + public AbstractAST visit(Multiply expression) { + return AST.multiply((org.iguana.datadependent.ast.Expression) expression.getLhs().accept(this), + (org.iguana.datadependent.ast.Expression) expression.getRhs().accept(this)); + } + + @Override + public AbstractAST visit(Divide expression) { + return AST.divide((org.iguana.datadependent.ast.Expression) expression.getLhs().accept(this), + (org.iguana.datadependent.ast.Expression) expression.getRhs().accept(this)); + + } + + @Override + public AbstractAST visit(Expression statement) { + return AST.stat((org.iguana.datadependent.ast.Expression) statement.getExpression().accept(this)); + } + + @Override + public Condition visit(DataDependentCondition condition) { + return DataDependentCondition.predicate( + (org.iguana.datadependent.ast.Expression) condition.getExpression().accept(this)); + } + + @Override + public Condition visit(PositionalCondition condition) { + return condition; + } + + @Override + public Condition visit(RegularExpressionCondition condition) { + return condition; + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/gss/CyclicDummyGSSEdges.java b/benchmarks/src/main/kotlin/org/iguana/gss/CyclicDummyGSSEdges.java new file mode 100644 index 000000000..d2c4b1a92 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/gss/CyclicDummyGSSEdges.java @@ -0,0 +1,59 @@ +package org.iguana.gss; + +import org.iguana.datadependent.env.Environment; +import org.iguana.grammar.slot.BodyGrammarSlot; +import org.iguana.result.Result; + +import java.util.ArrayList; +import java.util.List; + +/** + * GSSEdge that has the dummy node as its result, but in contrast to DummyGSSEdge, represents the special case of a + * direct cycle due to left recursion. + */ +public class CyclicDummyGSSEdges implements GSSEdge { + + private final List returnSlots; + + public CyclicDummyGSSEdges() { + returnSlots = new ArrayList<>(4); + } + + @Override + public T getResult() { + return null; + } + + @Override + public int getInputIndex() { + throw new UnsupportedOperationException(); + } + + @Override + public BodyGrammarSlot getReturnSlot() { + return null; + } + + @Override + public GSSNode getDestination() { + return null; + } + + @Override + public Environment getEnv() { + return null; + } + + List getReturnSlots() { + return returnSlots; + } + + void addReturnSlot(BodyGrammarSlot slot) { + returnSlots.add(slot); + } + + @Override + public String toString() { + return String.format("(%s, %s, %s)", returnSlots, "$", ""); + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/gss/CyclicDummyGSSEdgesWithEnv.java b/benchmarks/src/main/kotlin/org/iguana/gss/CyclicDummyGSSEdgesWithEnv.java new file mode 100644 index 000000000..83289e2fd --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/gss/CyclicDummyGSSEdgesWithEnv.java @@ -0,0 +1,18 @@ +package org.iguana.gss; + +import org.iguana.datadependent.env.Environment; +import org.iguana.result.Result; + +public class CyclicDummyGSSEdgesWithEnv extends CyclicDummyGSSEdges { + + private final Environment env; + + public CyclicDummyGSSEdgesWithEnv(Environment env) { + this.env = env; + } + + @Override + public Environment getEnv() { + return env; + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/gss/DefaultGSSEdge.java b/benchmarks/src/main/kotlin/org/iguana/gss/DefaultGSSEdge.java new file mode 100644 index 000000000..854fc4e94 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/gss/DefaultGSSEdge.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.gss; + +import org.iguana.datadependent.env.Environment; +import org.iguana.grammar.slot.BodyGrammarSlot; +import org.iguana.result.Result; + +public class DefaultGSSEdge implements GSSEdge { + + private final BodyGrammarSlot returnSlot; + private final T result; + private final GSSNode destination; + + public DefaultGSSEdge(BodyGrammarSlot returnSlot, T result, GSSNode destination) { + this.returnSlot = returnSlot; + this.result = result; + this.destination = destination; + } + + @Override + public T getResult() { + return result; + } + + @Override + public int getInputIndex() { + return result.getRightExtent(); + } + + @Override + public BodyGrammarSlot getReturnSlot() { + return returnSlot; + } + + @Override + public GSSNode getDestination() { + return destination; + } + + @Override + public Environment getEnv() { + return null; + } + + @Override + public String toString() { + return String.format("(%s, %s, %s)", returnSlot, result, destination); + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/gss/DefaultGSSEdgeWithEnv.java b/benchmarks/src/main/kotlin/org/iguana/gss/DefaultGSSEdgeWithEnv.java new file mode 100644 index 000000000..a8e9c2a55 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/gss/DefaultGSSEdgeWithEnv.java @@ -0,0 +1,20 @@ +package org.iguana.gss; + +import org.iguana.datadependent.env.Environment; +import org.iguana.grammar.slot.BodyGrammarSlot; +import org.iguana.result.Result; + +public class DefaultGSSEdgeWithEnv extends DefaultGSSEdge { + + private final Environment env; + + public DefaultGSSEdgeWithEnv(BodyGrammarSlot returnSlot, T result, GSSNode destination, Environment env) { + super(returnSlot, result, destination); + this.env = env; + } + + @Override + public Environment getEnv() { + return env; + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/gss/DefaultGSSNode.java b/benchmarks/src/main/kotlin/org/iguana/gss/DefaultGSSNode.java new file mode 100644 index 000000000..ef1d1b767 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/gss/DefaultGSSNode.java @@ -0,0 +1,338 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.gss; + +import org.iguana.datadependent.env.Environment; +import org.iguana.datadependent.env.EnvironmentPool; +import org.iguana.grammar.slot.BodyGrammarSlot; +import org.iguana.grammar.slot.EndGrammarSlot; +import org.iguana.grammar.slot.NonterminalGrammarSlot; +import org.iguana.grammar.slot.NonterminalTransition; +import org.iguana.parser.IguanaRuntime; +import org.iguana.result.Result; +import org.iguana.result.ResultOps; +import org.iguana.util.ParserLogger; +import org.iguana.utils.collections.Keys; +import org.iguana.utils.collections.OpenAddressingHashMap; +import org.iguana.utils.collections.key.Key; +import org.iguana.utils.input.Input; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +/** + * If there is a cyclic GSSEdge, it's always the first one. If there is a cyclic GSS edge, there is always + * a second GSS edge which is stored in restGSSEdges. + */ +public class DefaultGSSNode implements GSSNode { + + private final int inputIndex; + + private GSSEdge firstGSSEdge; + + private List> restGSSEdges; + + private T firstPoppedElement; + + private Map restPoppedElements; + + public DefaultGSSNode(GSSEdge firstGSSEdge, int inputIndex) { + this.firstGSSEdge = firstGSSEdge; + this.inputIndex = inputIndex; + } + + @Override + public void addGSSEdge( + Input input, + BodyGrammarSlot returnSlot, + int i, + GSSNode destination, + T w, + Environment env, + IguanaRuntime runtime) { + if (this == destination && w.isDummy()) { + if (!(firstGSSEdge instanceof CyclicDummyGSSEdges)) { + addGSSEdge(firstGSSEdge); + firstGSSEdge = runtime.createGSSEdge(returnSlot, w, null, env); + } + ParserLogger.getInstance().gssEdgeAdded(firstGSSEdge); + ((CyclicDummyGSSEdges) firstGSSEdge).addReturnSlot(returnSlot); + iterateOverPoppedElements(firstGSSEdge, returnSlot, destination, input, runtime); + } else { + GSSEdge edge = runtime.createGSSEdge(returnSlot, w, destination, env); + ParserLogger.getInstance().gssEdgeAdded(edge); + addGSSEdge(edge); + iterateOverPoppedElements(edge, returnSlot, destination, input, runtime); + } + } + + private void addGSSEdge(GSSEdge edge) { + if (restGSSEdges == null) { + restGSSEdges = new ArrayList<>(4); + } + restGSSEdges.add(edge); + } + + private void iterateOverPoppedElements( + GSSEdge edge, + BodyGrammarSlot returnSlot, + GSSNode destination, + Input input, + IguanaRuntime runtime) { + if (firstPoppedElement != null) processPoppedElement(firstPoppedElement, edge, returnSlot, destination, input, + runtime); + + if (restPoppedElements != null) { + for (T poppedElement : restPoppedElements.values()) { + processPoppedElement(poppedElement, edge, returnSlot, destination, input, runtime); + } + } + } + + public boolean pop(Input input, EndGrammarSlot slot, T child, IguanaRuntime runtime) { + return pop(input, slot, child, null, runtime); + } + + public boolean pop(Input input, EndGrammarSlot slot, T result, Object value, IguanaRuntime runtime) { + ParserLogger.getInstance().pop(this, result.getRightExtent(), result, value); + T node = addPoppedElements(slot, result, value, runtime.getResultOps()); + if (node != null) iterateOverEdges(input, node, runtime); + return node != null; + } + + /** + * Returns the newly created popped element, or null if the node already exists + */ + private T addPoppedElements(EndGrammarSlot slot, T child, Object value, ResultOps ops) { + // No node added yet + if (firstPoppedElement == null) { + firstPoppedElement = ops.convert(null, child, slot, value); + return firstPoppedElement; + } else { + int rightIndex = child.getRightExtent(); + + // Only one node is added and there is an ambiguity + if (rightIndex == firstPoppedElement.getRightExtent() && Objects.equals(value, + firstPoppedElement.getValue())) { + ops.convert(firstPoppedElement, child, slot, value); + } else { + Key key = value == null ? Keys.from(rightIndex) : Keys.from(rightIndex, value); + + if (restPoppedElements == null) { + restPoppedElements = new OpenAddressingHashMap<>(); + T poppedElement = ops.convert(null, child, slot, value); + restPoppedElements.put(key, poppedElement); + return poppedElement; + } + + T poppedElement = restPoppedElements.get(key); + if (poppedElement == null) { + poppedElement = ops.convert(null, child, slot, value); + restPoppedElements.put(key, poppedElement); + return poppedElement; + } + + ops.convert(poppedElement, child, slot, value); + } + return null; + } + } + + private void processPoppedElement( + T poppedElement, + GSSEdge edge, + BodyGrammarSlot returnSlot, + GSSNode destination, + Input input, + IguanaRuntime runtime) { + int rightExtent = poppedElement.getRightExtent(); + int nextChar = input.charAt(rightExtent); + if (returnSlot.testFollow(nextChar)) { + T result = addDescriptor(input, this, poppedElement, edge, returnSlot, runtime); + if (result != null) { + // TODO: verify if this fix is correct with more data-dependent examples. + // It seems like that some variables escape the scope and this is a problem for array-based environment + // implementations that rely on indexes. + runtime.scheduleDescriptor(returnSlot, destination, result, runtime.getEnvironment()); + } + } else { + String expectedCharacters = returnSlot.getFollowTest().toString(); + ParserLogger.getInstance().logFollowTestFailed(returnSlot, rightExtent, nextChar, expectedCharacters); + } + } + + private void iterateOverEdges(Input input, T result, IguanaRuntime runtime) { + if (firstGSSEdge instanceof CyclicDummyGSSEdges) { + List returnSlots = ((CyclicDummyGSSEdges) firstGSSEdge).getReturnSlots(); + for (int i = 0; i < returnSlots.size(); i++) { + processEdge(input, result, firstGSSEdge, returnSlots.get(i), runtime); + } + } else { + processEdge(input, result, firstGSSEdge, firstGSSEdge.getReturnSlot(), runtime); + } + + if (restGSSEdges != null) for (int i = 0; i < restGSSEdges.size(); i++) { + GSSEdge edge = restGSSEdges.get(i); + processEdge(input, result, edge, edge.getReturnSlot(), runtime); + } + } + + private void processEdge( + Input input, T node, GSSEdge edge, BodyGrammarSlot returnSlot, IguanaRuntime runtime) { + int rightExtent = node.getRightExtent(); + int nextChar = input.charAt(rightExtent); + if (!returnSlot.testFollow(nextChar)) { + ParserLogger.getInstance().logFollowTestFailed(returnSlot, rightExtent, nextChar, + returnSlot.getFollowTest().toString()); + return; + } + + T result = addDescriptor(input, this, node, edge, returnSlot, runtime); + if (result != null) { + Environment env = runtime.getEnvironment(); + runtime.scheduleDescriptor(returnSlot, edge.getDestination() != null ? edge.getDestination() : this, result, + env); + } + } + + /* + * + * Does the following: + * (1) checks conditions associated with the return slot + * (2) checks whether the descriptor to be created has been already created (and scheduled) before + * (2.1) if yes, returns null + * (2.2) if no, creates one and returns it + * + */ + private T addDescriptor( + Input input, + GSSNode source, + T result, + GSSEdge edge, + BodyGrammarSlot returnSlot, + IguanaRuntime runtime) { + int inputIndex = result.isDummy() ? source.getInputIndex() : result.getRightExtent(); + Environment env = edge.getEnv() == null ? runtime.getEmptyEnvironment() : edge.getEnv(); + GSSNode destination = edge.getDestination() != null ? edge.getDestination() : source; + + if (returnSlot.requiresBinding()) env = returnSlot.doBinding(result, env); + + runtime.setEnvironment(env); + + if (returnSlot.getConditions().execute(input, returnSlot, source, inputIndex, runtime.getEvaluatorContext(), + runtime)) { + EnvironmentPool.returnToPool(env); + return null; + } + + env = runtime.getEnvironment(); + + return returnSlot.getIntermediateNode(edge.getResult(), destination.getInputIndex(), result, env, runtime); + } + + public NonterminalGrammarSlot getGrammarSlot() { + NonterminalTransition transition; + if (firstGSSEdge instanceof CyclicDummyGSSEdges) { + transition = (NonterminalTransition) restGSSEdges.get(0).getReturnSlot().getInTransition(); + } else { + transition = (NonterminalTransition) firstGSSEdge.getReturnSlot().getInTransition(); + } + return transition.getSlot(); + } + + public int getInputIndex() { + return inputIndex; + } + + // TODO: find a way to evaluate the environment and the passed arguments + public Object[] getData() { + return null; + } + + public int countGSSEdges() { + int count = 0; + count += firstGSSEdge == null ? 0 : 1; + count += restGSSEdges == null ? 0 : restGSSEdges.size(); + return count; + } + + public int countPoppedElements() { + int count = 0; + if (firstPoppedElement != null) count++; + if (restPoppedElements != null) count += restPoppedElements.size(); + return count; + } + + public Iterable> getGSSEdges() { + int size = countGSSEdges(); + if (size == 0) return Collections.emptyList(); + List> gssEdges = new ArrayList<>(size); + if (firstGSSEdge != null) { + gssEdges.add(firstGSSEdge); + } + if (restGSSEdges != null) { + gssEdges.addAll(restGSSEdges); + } + return gssEdges; + } + + public boolean equals(Object obj) { + if (this == obj) return true; + + if (!(obj instanceof GSSNode)) return false; + + GSSNode other = (GSSNode) obj; + + return getGrammarSlot() == other.getGrammarSlot() && getInputIndex() == other.getInputIndex() && Arrays.equals( + getData(), other.getData()); + } + + public int hashCode() { + return Objects.hash(getGrammarSlot().hashCode(), getInputIndex(), Arrays.hashCode(getData())); + } + + public Iterable getPoppedElements() { + List poppedElements = new ArrayList<>(countPoppedElements()); + if (firstPoppedElement != null) poppedElements.add(firstPoppedElement); + if (restPoppedElements != null) poppedElements.addAll(restPoppedElements.values()); + + return poppedElements; + } + + public String toString() { + String s = String.format("(%s, %d)", getGrammarSlot(), getInputIndex()); + if (getData() != null) { + s += String.format("(%s)", getData()); + } + return s; + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/gss/DummyGSSEdge.java b/benchmarks/src/main/kotlin/org/iguana/gss/DummyGSSEdge.java new file mode 100644 index 000000000..9037fff29 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/gss/DummyGSSEdge.java @@ -0,0 +1,51 @@ +package org.iguana.gss; + +import org.iguana.datadependent.env.Environment; +import org.iguana.grammar.slot.BodyGrammarSlot; +import org.iguana.result.Result; + +/** + * GSSEdge that has the dummy node as its result. + * The inputIndex of these GSS instances are the same as the input index of the destination GSS node because + * the call is being made at the same input index. + */ +public class DummyGSSEdge implements GSSEdge { + + private final BodyGrammarSlot returnSlot; + private final GSSNode destination; + + public DummyGSSEdge(BodyGrammarSlot returnSlot, GSSNode destination) { + this.returnSlot = returnSlot; + this.destination = destination; + } + + @Override + public T getResult() { + return null; + } + + @Override + public BodyGrammarSlot getReturnSlot() { + return returnSlot; + } + + @Override + public GSSNode getDestination() { + return destination; + } + + @Override + public Environment getEnv() { + return null; + } + + @Override + public int getInputIndex() { + return destination.getInputIndex(); + } + + @Override + public String toString() { + return String.format("(%s, %s, %s)", returnSlot, "$", destination); + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/gss/DummyGSSEdgeWithEnv.java b/benchmarks/src/main/kotlin/org/iguana/gss/DummyGSSEdgeWithEnv.java new file mode 100644 index 000000000..2681a6155 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/gss/DummyGSSEdgeWithEnv.java @@ -0,0 +1,20 @@ +package org.iguana.gss; + +import org.iguana.datadependent.env.Environment; +import org.iguana.grammar.slot.BodyGrammarSlot; +import org.iguana.result.Result; + +public class DummyGSSEdgeWithEnv extends DummyGSSEdge { + + private final Environment env; + + public DummyGSSEdgeWithEnv(BodyGrammarSlot returnSlot, GSSNode destination, Environment env) { + super(returnSlot, destination); + this.env = env; + } + + @Override + public Environment getEnv() { + return env; + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/gss/GSSEdge.java b/benchmarks/src/main/kotlin/org/iguana/gss/GSSEdge.java new file mode 100644 index 000000000..fa8fdfaef --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/gss/GSSEdge.java @@ -0,0 +1,19 @@ +package org.iguana.gss; + +import org.iguana.datadependent.env.Environment; +import org.iguana.grammar.slot.BodyGrammarSlot; +import org.iguana.result.Result; + +public interface GSSEdge { + + T getResult(); + + int getInputIndex(); + + BodyGrammarSlot getReturnSlot(); + + GSSNode getDestination(); + + Environment getEnv(); + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/gss/GSSNode.java b/benchmarks/src/main/kotlin/org/iguana/gss/GSSNode.java new file mode 100644 index 000000000..fed89e250 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/gss/GSSNode.java @@ -0,0 +1,39 @@ +package org.iguana.gss; + +import org.iguana.datadependent.env.Environment; +import org.iguana.grammar.slot.BodyGrammarSlot; +import org.iguana.grammar.slot.EndGrammarSlot; +import org.iguana.grammar.slot.NonterminalGrammarSlot; +import org.iguana.parser.IguanaRuntime; +import org.iguana.result.Result; +import org.iguana.utils.input.Input; + +public interface GSSNode { + + NonterminalGrammarSlot getGrammarSlot(); + + int getInputIndex(); + + Object[] getData(); + + void addGSSEdge( + Input input, + BodyGrammarSlot returnSlot, + int i, + GSSNode destination, + T w, + Environment env, + IguanaRuntime runtime); + + boolean pop(Input input, EndGrammarSlot slot, T child, IguanaRuntime runtime); + + boolean pop(Input input, EndGrammarSlot slot, T result, Object value, IguanaRuntime runtime); + + int countGSSEdges(); + + int countPoppedElements(); + + Iterable> getGSSEdges(); + + Iterable getPoppedElements(); +} diff --git a/benchmarks/src/main/kotlin/org/iguana/gss/StartGSSNode.java b/benchmarks/src/main/kotlin/org/iguana/gss/StartGSSNode.java new file mode 100644 index 000000000..98f70801c --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/gss/StartGSSNode.java @@ -0,0 +1,116 @@ +package org.iguana.gss; + +import org.iguana.datadependent.env.Environment; +import org.iguana.grammar.slot.BodyGrammarSlot; +import org.iguana.grammar.slot.EndGrammarSlot; +import org.iguana.grammar.slot.NonterminalGrammarSlot; +import org.iguana.parser.IguanaRuntime; +import org.iguana.result.Result; +import org.iguana.util.ParserLogger; +import org.iguana.utils.collections.primitive.IntHashMap; +import org.iguana.utils.collections.primitive.OpenAddressingIntHashMap; +import org.iguana.utils.input.Input; + +import java.util.Collections; + +public class StartGSSNode implements GSSNode { + + private final NonterminalGrammarSlot slot; + private final int inputIndex; + private final Object[] arguments; + + private final IntHashMap poppedElements; + + public StartGSSNode(NonterminalGrammarSlot slot, int inputIndex) { + this(slot, inputIndex, null); + } + + public StartGSSNode(NonterminalGrammarSlot slot, int inputIndex, Object[] arguments) { + this.slot = slot; + this.inputIndex = inputIndex; + this.arguments = arguments; + poppedElements = new OpenAddressingIntHashMap<>(); + } + + @Override + public NonterminalGrammarSlot getGrammarSlot() { + return slot; + } + + @Override + public int getInputIndex() { + return inputIndex; + } + + @Override + public Object[] getData() { + return null; + } + + @Override + public void addGSSEdge( + Input input, + BodyGrammarSlot returnSlot, + int i, + GSSNode destination, + T w, + Environment env, + IguanaRuntime runtime) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean pop(Input input, EndGrammarSlot slot, T result, IguanaRuntime runtime) { + return pop(input, slot, result, null, runtime); + } + + @Override + public boolean pop(Input input, EndGrammarSlot slot, T result, Object value, IguanaRuntime runtime) { + ParserLogger.getInstance().pop(this, result.getLeftExtent(), result, value); + + int index = result.getRightExtent(); + T poppedElement = poppedElements.get(index); + + if (poppedElement == null) { + poppedElement = runtime.getResultOps().convert(null, result, slot, value); + poppedElements.put(index, poppedElement); + return true; + } else { + runtime.getResultOps().convert(poppedElement, result, slot, value); + return false; + } + } + + public T getResult(int i) { + return poppedElements.get(i); + } + + @Override + public int countGSSEdges() { + return 0; + } + + @Override + public int countPoppedElements() { + return poppedElements.size(); + } + + @Override + public Iterable> getGSSEdges() { + return Collections.emptyList(); + } + + @Override + public Iterable getPoppedElements() { + return poppedElements.values(); + } + + @Override + public String toString() { + return String.format("(%s, %d)", slot, inputIndex); + } + + public Object[] getArguments() { + return arguments; + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/iggy/IggyParseTreeToGrammarVisitor.java b/benchmarks/src/main/kotlin/org/iguana/iggy/IggyParseTreeToGrammarVisitor.java new file mode 100644 index 000000000..3f88b5ecc --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/iggy/IggyParseTreeToGrammarVisitor.java @@ -0,0 +1,894 @@ +package org.iguana.iggy; + +import org.iguana.datadependent.ast.AST; +import org.iguana.datadependent.ast.Expression; +import org.iguana.datadependent.ast.Statement; +import org.iguana.grammar.Grammar; +import org.iguana.grammar.condition.DataDependentCondition; +import org.iguana.grammar.condition.PositionalCondition; +import org.iguana.grammar.condition.RegularExpressionCondition; +import org.iguana.grammar.slot.NonterminalNodeType; +import org.iguana.grammar.slot.TerminalNodeType; +import org.iguana.grammar.symbol.AbstractSymbol; +import org.iguana.grammar.symbol.Align; +import org.iguana.grammar.symbol.Alt; +import org.iguana.grammar.symbol.Alternative; +import org.iguana.grammar.symbol.Associativity; +import org.iguana.grammar.symbol.Code; +import org.iguana.grammar.symbol.Error; +import org.iguana.grammar.symbol.Group; +import org.iguana.grammar.symbol.Identifier; +import org.iguana.grammar.symbol.IfThenElse; +import org.iguana.grammar.symbol.Ignore; +import org.iguana.grammar.symbol.LayoutStrategy; +import org.iguana.grammar.symbol.Nonterminal; +import org.iguana.grammar.symbol.Offside; +import org.iguana.grammar.symbol.Opt; +import org.iguana.grammar.symbol.Plus; +import org.iguana.grammar.symbol.PriorityLevel; +import org.iguana.grammar.symbol.Return; +import org.iguana.grammar.symbol.Rule; +import org.iguana.grammar.symbol.Sequence; +import org.iguana.grammar.symbol.Star; +import org.iguana.grammar.symbol.Start; +import org.iguana.grammar.symbol.Symbol; +import org.iguana.grammar.symbol.SymbolBuilder; +import org.iguana.grammar.symbol.Terminal; +import org.iguana.iggy.gen.IggyParseTree; +import org.iguana.iggy.gen.IggyParseTree.RegexRule; +import org.iguana.iggy.gen.IggyParseTreeVisitor; +import org.iguana.parsetree.ParseTreeNode; +import org.iguana.regex.Char; +import org.iguana.regex.CharRange; +import org.iguana.regex.RegularExpression; +import org.iguana.regex.Seq; +import org.iguana.util.Tuple; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.function.Function; +import java.util.stream.Collectors; + +import static org.iguana.utils.collections.CollectionsUtil.flatten; + +public class IggyParseTreeToGrammarVisitor implements IggyParseTreeVisitor { + + // A map from the name of the regular expressions (as they are defined in the grammar) to the regular expression. + private final Map regularExpressionDefinitions = new LinkedHashMap<>(); + + private final Map literals = new LinkedHashMap<>(); + + private final List startSymbols = new ArrayList<>(); + private Identifier layout; + + @Override + public Grammar visitGrammar(IggyParseTree.Grammar node) { + Optional name = visit(node.name()); + Grammar.Builder builder = new Grammar.Builder(); + List defs = visit(node.defs()); + + final Map globals = new HashMap<>(); + + for (Object def : defs) { + if (def instanceof Rule) { + builder.addRule((Rule) def); + } else { // Tuple var = (Tuple) def; + globals.put(var.getFirst(), var.getSecond()); + } + } + for (Map.Entry entry : regularExpressionDefinitions.entrySet()) { + builder.addRegularExpression(entry.getKey(), entry.getValue()); + } + for (Map.Entry entry : globals.entrySet()) { + builder.addGlobal(entry.getKey(), entry.getValue()); + } + for (Map.Entry entry : literals.entrySet()) { + builder.addLiteral(entry.getKey(), entry.getValue()); + } + for (String start : startSymbols) { + builder.addStartSymbol(Start.from(start)); + } + builder.setLayout(layout); + name.ifPresent(identifier -> builder.setName(identifier.getName())); + return builder.build(); + } + + @Override + public Tuple visitTopLevelVar(IggyParseTree.TopLevelVar node) { + String key = node.id().getText(); + Expression value = (Expression) node.exp().accept(this); + return Tuple.of(key, value); + } + + @Override + public Rule visitContextFreeRule(IggyParseTree.ContextFreeRule node) { + Identifier nonterminalName = (Identifier) node.name().accept(this); + Optional> parameters = visit(node.params()); + List priorityLevels = visit(node.body()); + + LayoutStrategy layoutStrategy = LayoutStrategy.INHERITED; + if (node.modifier().hasChildren()) { // start symbol + String text = node.modifier().getText(); + if (text.equals("start")) { + startSymbols.add(nonterminalName.getName()); + } else if (text.equals("lexical")) { + layoutStrategy = LayoutStrategy.NO_LAYOUT; + } else { // "layout" + layout = nonterminalName; + layoutStrategy = LayoutStrategy.NO_LAYOUT; + } + } + + NonterminalNodeType nonterminalNodeType; + if (layout == null) { + nonterminalNodeType = NonterminalNodeType.Basic; + } else { + if (layout.getName().equals(nonterminalName.getName())) { + nonterminalNodeType = NonterminalNodeType.Layout; + } else { + nonterminalNodeType = NonterminalNodeType.Basic; + } + } + Nonterminal nonterminal = new Nonterminal.Builder(nonterminalName.getName()) + .addParameters(parameters.map(identifiers -> identifiers.stream().map(AbstractSymbol::toString) + .collect(Collectors.toList())).orElse(Collections.emptyList())) + .setNodeType(nonterminalNodeType) + .build(); + + return new Rule.Builder(nonterminal) + .addPriorityLevels(priorityLevels) + .setLayoutStrategy(layoutStrategy) + .build(); + } + + @Override + public Void visitRegexRule(RegexRule node) { + List> alts = visit(node.body()); + Identifier identifier = (Identifier) node.name().accept(this); + regularExpressionDefinitions.put(identifier.getName(), getRegex(alts)); + + if (node.modifier().hasChildren()) { + layout = identifier; + } + + return null; + } + + @Override + public List visitParameters(IggyParseTree.Parameters node) { + return visitChildren(node); + } + + @Override + public Object visitRegexBody(IggyParseTree.RegexBody node) { + return visitChildren(node); + } + + @Override + public Object visitBody(IggyParseTree.Body node) { + return visitChildren(node); + } + + @Override + public PriorityLevel visitPriorityLevels(IggyParseTree.PriorityLevels node) { + PriorityLevel.Builder builder = new PriorityLevel.Builder(); + builder.addAlternatives(visitChildren(node)); + return builder.build(); + } + + @Override + public Alternative visitSequenceAlternative(IggyParseTree.SequenceAlternative node) { + Alternative.Builder builder = new Alternative.Builder(); + builder.addSequence((Sequence) node.seq().accept(this)); + return builder.build(); + } + + @Override + public Alternative visitAssociativityAlternative(IggyParseTree.AssociativityAlternative node) { + Associativity associativity = getAssociativity(node.assoc()); + List seqs = visit(node.seqs()); + return new Alternative.Builder(seqs, associativity).build(); + } + + @Override + public Sequence visitMoreThanOneElemSequence(IggyParseTree.MoreThanOneElemSequence node) { + Sequence.Builder builder = new Sequence.Builder(); + + if (node.assoc().hasChildren()) { + builder.setAssociativity(getAssociativity(node.assoc())); + } + + Optional> expressions = visit(node.cond()); + List symbols = new ArrayList<>(); + Symbol symbol = (Symbol) node.first().accept(this); + SymbolBuilder symbolBuilder = symbol.copy(); + if (expressions.isPresent()) { + for (Expression expression : expressions.get()) { + symbolBuilder.addPreCondition(DataDependentCondition.predicate(expression)); + } + } + symbols.add(symbolBuilder.build()); + symbols.addAll(visit(node.rest())); + builder.addSymbols(symbols); + Optional returnExpression = visit(node.ret()); + if (returnExpression.isPresent()) { + builder.addSymbol(Return.ret(returnExpression.get())); + } + Optional label = visit(node.label()); + if (label.isPresent()) { + builder.setLabel(label.get()); + } + return builder.build(); + } + + @Override + public Sequence visitSingleElemSequence(IggyParseTree.SingleElemSequence node) { + Sequence.Builder builder = new Sequence.Builder(); + Optional> expressions = visit(node.cond()); + Symbol symbol = (Symbol) node.sym().accept(this); + SymbolBuilder symbolBuilder = symbol.copy(); + if (expressions.isPresent()) { + for (Expression expression : expressions.get()) { + symbolBuilder.addPreCondition(DataDependentCondition.predicate(expression)); + } + } + Optional returnExpression = visit(node.ret()); + if (returnExpression.isPresent()) { + builder.addSymbol(Return.ret(returnExpression.get())); + } + builder.addSymbol(symbolBuilder.build()); + Optional label = visit(node.label()); + if (label.isPresent()) { + builder.setLabel(label.get()); + } + return builder.build(); + } + + @Override + public Sequence visitEmptySequence(IggyParseTree.EmptySequence node) { + Optional label = visit(node.label()); + return new Sequence.Builder().setLabel(label.orElse(null)).build(); + } + + @Override + public Object visitCondition(IggyParseTree.Condition node) { + return visitChildren(node); + } + + @Override + public Nonterminal visitCallSymbol(IggyParseTree.CallSymbol node) { + Expression[] expressions = ((List) node.args().accept(this)).toArray(new Expression[]{}); + Identifier id = (Identifier) node.id().accept(this); + return new Nonterminal.Builder(id.getName()).apply(expressions).build(); + } + + @Override + public Offside visitOffsideSymbol(IggyParseTree.OffsideSymbol node) { + return Offside.offside((Symbol) node.sym().accept(this)); + } + + @Override + public Star visitStarSymbol(IggyParseTree.StarSymbol node) { + return Star.from((Symbol) node.sym().accept(this)); + } + + @Override + public Plus visitPlusSymbol(IggyParseTree.PlusSymbol node) { + return Plus.from((Symbol) node.sym().accept(this)); + } + + @Override + public Opt visitOptionSymbol(IggyParseTree.OptionSymbol node) { + return Opt.from((Symbol) node.sym().accept(this)); + } + + @Override + public Group visitSequenceSymbol(IggyParseTree.SequenceSymbol node) { + List symbols = visit(node.syms()); + return Group.from(symbols); + } + + @Override + public Alt visitAlternationSymbol(IggyParseTree.AlternationSymbol node) { + List symbols = new ArrayList<>(); + List first = visit(node.first()); + List> second = visit(node.rest()); + if (first.size() == 1) { + symbols.add(first.get(0)); + } else { + symbols.add(Group.from(first)); + } + for (List list : second) { + if (list.size() == 1) { + symbols.add(list.get(0)); + } else { + symbols.add(Group.from(list)); + } + } + return Alt.from(symbols); + } + + @Override + public Align visitAlignSymbol(IggyParseTree.AlignSymbol node) { + return Align.align((Symbol) node.sym().accept(this)); + } + + @Override + public Ignore visitIgnoreSymbol(IggyParseTree.IgnoreSymbol node) { + return Ignore.ignore((Symbol) node.sym().accept(this)); + } + + @Override + public Symbol visitLabeledSymbol(IggyParseTree.LabeledSymbol node) { + Symbol symbol = (Symbol) node.sym().accept(this); + Identifier id = (Identifier) node.id().accept(this); + return symbol.copy().setLabel(id.getName()).build(); + } + + @Override + public Symbol visitStatementSymbol(IggyParseTree.StatementSymbol node) { + Symbol symbol = (Symbol) node.sym().accept(this); + List> statements = visit(node.stmts()); + return Code.code(symbol, flatten(statements).toArray(new Statement[0])); + } + + @Override + public Symbol visitPostConditionSymbol(IggyParseTree.PostConditionSymbol node) { + Symbol symbol = (Symbol) node.sym().accept(this); + List expressions = visit(node.cond()); + SymbolBuilder builder = symbol.copy(); + for (Expression expression : expressions) { + builder.addPostCondition(DataDependentCondition.predicate(expression)); + } + return builder.build(); + } + + @Override + public Symbol visitPrecedeSymbol(IggyParseTree.PrecedeSymbol node) { + Symbol symbol = (Symbol) node.sym().accept(this); + RegularExpression regex = (RegularExpression) node.reg().accept(this); + return symbol.copy().addPreCondition(RegularExpressionCondition.precede(regex)).build(); + } + + @Override + public Symbol visitNotPrecedeSymbol(IggyParseTree.NotPrecedeSymbol node) { + Symbol symbol = (Symbol) node.sym().accept(this); + RegularExpression regex = (RegularExpression) node.reg().accept(this); + return symbol.copy().addPreCondition(RegularExpressionCondition.notPrecede(regex)).build(); + } + + @Override + public Symbol visitStartOfLineSymbol(IggyParseTree.StartOfLineSymbol node) { + Symbol symbol = (Symbol) node.sym().accept(this); + return symbol.copy().addPreCondition(PositionalCondition.startOfLineCondition()).build(); + } + + @Override + public Symbol visitFollowSymbol(IggyParseTree.FollowSymbol node) { + Symbol symbol = (Symbol) node.sym().accept(this); + RegularExpression regex = (RegularExpression) node.reg().accept(this); + return symbol.copy().addPostCondition(RegularExpressionCondition.follow(regex)).build(); + } + + @Override + public Symbol visitNotFollowSymbol(IggyParseTree.NotFollowSymbol node) { + Symbol symbol = (Symbol) node.sym().accept(this); + RegularExpression regex = (RegularExpression) node.reg().accept(this); + return symbol.copy().addPostCondition(RegularExpressionCondition.notFollow(regex)).build(); + } + + @Override + public Symbol visitExcludeSymbol(IggyParseTree.ExcludeSymbol node) { + Symbol symbol = (Symbol) node.sym().accept(this); + RegularExpression regex = (RegularExpression) node.reg().accept(this); + return symbol.copy().addPostCondition(RegularExpressionCondition.notMatch(regex)).build(); + } + + @Override + public Symbol visitExceptSymbol(IggyParseTree.ExceptSymbol node) { + Symbol symbol = (Symbol) node.sym().accept(this); + Identifier id = (Identifier) node.id().accept(this); + if (symbol instanceof Identifier) { + return ((Identifier) symbol).copy().addExcept(id.getName()).build(); + } else { + // TODO: I think nonterminal calls are also allowed here, handle it. + throw new RuntimeException("Unexpected symbol"); + } + } + + @Override + public Symbol visitEndOfLineSymbol(IggyParseTree.EndOfLineSymbol node) { + Symbol symbol = (Symbol) node.sym().accept(this); + return symbol.copy().addPostCondition(PositionalCondition.endOfLineCondition()).build(); + } + + @Override + public Symbol visitEndOfFileSymbol(IggyParseTree.EndOfFileSymbol node) { + Symbol symbol = (Symbol) node.sym().accept(this); + return symbol.copy().addPostCondition(PositionalCondition.endOfFileCondition()).build(); + } + + @Override + public Symbol visitIfThenElseSymbol(IggyParseTree.IfThenElseSymbol node) { + return IfThenElse.ifThenElse( + (Expression) node.exp().accept(this), + (Symbol) node.thenPart().accept(this), + (Symbol) node.elsePart().accept(this) + ); + } + + @Override + public Identifier visitIdentifierSymbol(IggyParseTree.IdentifierSymbol node) { + return (Identifier) node.id().accept(this); + } + + @Override + public Terminal visitStringSymbol(IggyParseTree.StringSymbol node) { + String text = stripQuotes(node); + RegularExpression regex = getCharsRegex(text); + literals.put(text, regex); + return new Terminal.Builder(regex) + .setNodeType(TerminalNodeType.Literal) + .build(); + } + + @Override + public Terminal visitCharClassSymbol(IggyParseTree.CharClassSymbol node) { + RegularExpression regex = (RegularExpression) node.charClass().accept(this); + return new Terminal.Builder(regex) + .setNodeType(TerminalNodeType.Regex) + .build(); + } + + @Override + public Star visitStarSepSymbol(IggyParseTree.StarSepSymbol node) { + Symbol symbol = (Symbol) node.sym().accept(this); + List seps = visit(node.sep()); + return new Star.Builder(symbol).addSeparators(seps).build(); + } + + @Override + public Plus visitPlusSepSymbol(IggyParseTree.PlusSepSymbol node) { + Symbol symbol = (Symbol) node.sym().accept(this); + List seps = visit(node.sep()); + return new Plus.Builder(symbol).addSeparators(seps).build(); + } + + @Override + public Object visitErrorSymbol(IggyParseTree.ErrorSymbol node) { + return Error.getInstance(); + } + + @Override + public List visitArguments(IggyParseTree.Arguments node) { + return visitChildren(node); + } + + @Override + public Object visitCallStatement(IggyParseTree.CallStatement node) { + Function fun = (Function) node.fun().accept(this); + Expression[] arguments = ((List) node.args().accept(this)).toArray(new Expression[]{}); + return Collections.singletonList(AST.stat(fun.apply(arguments))); + } + + @Override + public List visitBindingStatement(IggyParseTree.BindingStatement node) { + return visit(node.bindings()); + } + + @Override + public List visitAssignBinding(IggyParseTree.AssignBinding node) { + List statements = new ArrayList<>(); + Identifier id = (Identifier) node.varName().accept(this); + statements.add(AST.stat(AST.assign(id.getName(), (Expression) node.exp().accept(this)))); + return statements; + } + + @Override + public List visitDeclareBinding(IggyParseTree.DeclareBinding node) { + List statements = new ArrayList<>(); + List elems = visit(node.decls()); + int i = 0; + while (i < elems.size()) { + statements.add(AST.varDeclStat(((Identifier) elems.get(i)).getName(), (Expression) elems.get(i + 1))); + i += 2; + } + return statements; + } + + @Override + public org.iguana.regex.Star visitStarRegex(IggyParseTree.StarRegex node) { + return org.iguana.regex.Star.from((RegularExpression) node.reg().accept(this)); + } + + @Override + public org.iguana.regex.Plus visitPlusRegex(IggyParseTree.PlusRegex node) { + return org.iguana.regex.Plus.from((RegularExpression) node.reg().accept(this)); + } + + @Override + public org.iguana.regex.Opt visitOptionRegex(IggyParseTree.OptionRegex node) { + return org.iguana.regex.Opt.from((RegularExpression) node.reg().accept(this)); + } + + @Override + public RegularExpression visitBracketRegex(IggyParseTree.BracketRegex node) { + return (RegularExpression) node.reg().accept(this); + } + + @Override + public org.iguana.regex.Seq visitSequenceRegex(IggyParseTree.SequenceRegex node) { + List list = new ArrayList<>(); + list.add((RegularExpression) node.first().accept(this)); + list.addAll(visit(node.rest())); + return org.iguana.regex.Seq.from(list); + } + + @Override + public org.iguana.regex.Alt visitAlternationRegex(IggyParseTree.AlternationRegex node) { + List first = visit(node.first()); + List> rest = visit(node.rest()); + List res = new ArrayList<>(first); + res.addAll(rest.stream().map(l -> { + if (l.size() == 1) return l.get(0); + else return Seq.from(l); + }).collect(Collectors.toList())); + return org.iguana.regex.Alt.from(res); + } + + @Override + public org.iguana.regex.Reference visitNontRegex(IggyParseTree.NontRegex node) { + Identifier id = (Identifier) node.name().accept(this); + return org.iguana.regex.Reference.from(id.getName()); + } + + @Override + public org.iguana.regex.Alt visitCharClassRegex(IggyParseTree.CharClassRegex node) { + return visit(node.charClass()); + } + + @Override + public RegularExpression visitStringRegex(IggyParseTree.StringRegex node) { + return getCharsRegex(stripQuotes(node)); + } + + @Override + public org.iguana.regex.Alt visitCharsCharClass(IggyParseTree.CharsCharClass node) { + return org.iguana.regex.Alt.from((List) node.ranges().accept(this)); + } + + @Override + public org.iguana.regex.Alt visitNotCharsCharClass(IggyParseTree.NotCharsCharClass node) { + return org.iguana.regex.Alt.not((List) node.ranges().accept(this)); + } + + @Override + public CharRange visitRangeRange(IggyParseTree.RangeRange node) { + int start = getRangeChar(node.first().getText()); + int end = getRangeChar(node.second().getText()); + return CharRange.in(start, end); + } + + @Override + public Char visitCharacterRange(IggyParseTree.CharacterRange node) { + int c = getRangeChar(node.range().getText()); + return Char.from(c); + } + + @Override + public Expression.Call visitCallExpression(IggyParseTree.CallExpression node) { + Function fun = (Function) node.fun().accept(this); + Expression[] arguments = ((List) node.args().accept(this)).toArray(new Expression[]{}); + return fun.apply(arguments); + } + + @Override + public Expression.Not visitNotExpression(IggyParseTree.NotExpression node) { + Expression exp = visit(node.exp()); + return AST.not(exp); + } + + @Override + public Expression.Multiply visitMultiplicationExpression(IggyParseTree.MultiplicationExpression node) { + Expression lhs = visit(node.lhs()); + Expression rhs = visit(node.rhs()); + return AST.multiply(lhs, rhs); + } + + @Override + public Expression.Divide visitDivisionExpression(IggyParseTree.DivisionExpression node) { + Expression lhs = visit(node.lhs()); + Expression rhs = visit(node.rhs()); + return AST.divide(lhs, rhs); + } + + @Override + public Expression.Add visitAdditionExpression(IggyParseTree.AdditionExpression node) { + Expression lhs = visit(node.lhs()); + Expression rhs = visit(node.rhs()); + return AST.add(lhs, rhs); + } + + @Override + public Expression.Subtract visitSubtractionExpression(IggyParseTree.SubtractionExpression node) { + Expression lhs = visit(node.lhs()); + Expression rhs = visit(node.rhs()); + return AST.subtract(lhs, rhs); + } + + @Override + public Expression.GreaterThanEqual visitGreaterEqExpression(IggyParseTree.GreaterEqExpression node) { + Expression lhs = visit(node.lhs()); + Expression rhs = visit(node.rhs()); + return AST.greaterEq(lhs, rhs); + } + + @Override + public Expression.LessThanEqual visitLessEqExpression(IggyParseTree.LessEqExpression node) { + Expression lhs = visit(node.lhs()); + Expression rhs = visit(node.rhs()); + return AST.lessEq(lhs, rhs); + } + + @Override + public Expression.Greater visitGreaterExpression(IggyParseTree.GreaterExpression node) { + Expression lhs = visit(node.lhs()); + Expression rhs = visit(node.rhs()); + return AST.greater(lhs, rhs); + } + + @Override + public Expression.Less visitLessExpression(IggyParseTree.LessExpression node) { + Expression lhs = visit(node.lhs()); + Expression rhs = visit(node.rhs()); + return AST.less(lhs, rhs); + } + + @Override + public Expression.Equal visitEqualExpression(IggyParseTree.EqualExpression node) { + Expression lhs = visit(node.lhs()); + Expression rhs = visit(node.rhs()); + return AST.equal(lhs, rhs); + } + + @Override + public Expression.NotEqual visitNotEqualExpression(IggyParseTree.NotEqualExpression node) { + Expression lhs = visit(node.lhs()); + Expression rhs = visit(node.rhs()); + return AST.notEqual(lhs, rhs); + } + + @Override + public Expression.And visitAndExpression(IggyParseTree.AndExpression node) { + Expression lhs = visit(node.lhs()); + Expression rhs = visit(node.rhs()); + return AST.and(lhs, rhs); + } + + @Override + public Expression.Or visitOrExpression(IggyParseTree.OrExpression node) { + Expression lhs = visit(node.lhs()); + Expression rhs = visit(node.rhs()); + return AST.or(lhs, rhs); + } + + @Override + public Expression.LeftExtent visitLExtentExpression(IggyParseTree.LExtentExpression node) { + String l = node.id().getText(); + return AST.lExt(l); + } + + @Override + public Expression.RightExtent visitRExtentExpression(IggyParseTree.RExtentExpression node) { + String r = node.id().getText(); + return AST.rExt(r); + } + + @Override + public Expression.Yield visitYieldExpression(IggyParseTree.YieldExpression node) { + String yield = node.id().getText(); + return AST.yield(yield); + } + + @Override + public Expression.Val visitValExpression(IggyParseTree.ValExpression node) { + String val = node.id().getText(); + return AST.val(val); + } + + @Override + public Expression.Name visitNameExpression(IggyParseTree.NameExpression node) { + return AST.var((node.varName().getText())); + } + + @Override + public Expression.Integer visitNumberExpression(IggyParseTree.NumberExpression node) { + return AST.integer(Integer.parseInt(node.number().getText())); + } + + @Override + public Expression visitBracketExpression(IggyParseTree.BracketExpression node) { + return (Expression) node.exp().accept(this); + } + + @Override + public Expression visitReturnExpression(IggyParseTree.ReturnExpression node) { + return (Expression) node.exp().accept(this); + } + + @Override + public Identifier visitVarName(IggyParseTree.VarName node) { + return (Identifier) node.id().accept(this); + } + + @Override + public String visitLabel(IggyParseTree.Label node) { + Identifier id = (Identifier) node.id().accept(this); + return id.getName(); + } + + @Override + public Object visitLayout(IggyParseTree.Layout node) { + throw new RuntimeException("Layout is handled in ContextFreeRule, this method should not be called"); + } + + @Override + public Identifier visitName(IggyParseTree.Name node) { + return (Identifier) node.id().accept(this); + } + + @Override + public Function visitPrintlnFunName(IggyParseTree.PrintlnFunName node) { + return AST::println; + } + + @Override + public Function visitIndentFunName(IggyParseTree.IndentFunName node) { + return AST::indent; + } + + @Override + public Function visitAssertFunName(IggyParseTree.AssertFunName node) { + return AST::assertion; + } + + @Override + public Function visitSetFunName(IggyParseTree.SetFunName node) { + return AST::set; + } + + @Override + public Function visitContainsFunName(IggyParseTree.ContainsFunName node) { + return AST::contains; + } + + @Override + public Function visitPutFunName(IggyParseTree.PutFunName node) { + return AST::put; + } + + @Override + public Identifier visitIdentifier(IggyParseTree.Identifier node) { + return Identifier.fromName(node.getText()); + } + + private T visit(ParseTreeNode node) { + return (T) node.accept(this); + } + + private static RegularExpression getRegex(List> listOfList) { + if (listOfList.size() == 1) { + return getRegexOfList(listOfList.get(0)); + } + org.iguana.regex.Alt.Builder builder = new org.iguana.regex.Alt.Builder<>(); + for (List list : listOfList) { + builder.add(getRegexOfList(list)); + } + return builder.build(); + } + + private static RegularExpression getRegexOfList(List list) { + if (list.size() == 1) { + return list.get(0); + } + return org.iguana.regex.Seq.from(list); + } + + private static Associativity getAssociativity(ParseTreeNode node) { + switch (node.getText()) { + case "left": + return Associativity.LEFT; + case "right": + return Associativity.RIGHT; + case "non-assoc": + return Associativity.NON_ASSOC; + default: + return Associativity.UNDEFINED; + } + } + + private static String stripQuotes(ParseTreeNode node) { + return node.getText().substring(1, node.getText().length() - 1); + } + + private static int[] getChars(String s) { + int i = 0; + int j = 0; + int[] chars = new int[s.length()]; + while (i < s.length()) { + if (s.charAt(i) == '\\') { + switch (s.charAt(i + 1)) { + case 'n': + chars[j++] = '\n'; + break; + case 'r': + chars[j++] = '\r'; + break; + case 't': + chars[j++] = '\t'; + break; + case 'f': + chars[j++] = '\f'; + break; + case ' ': + chars[j++] = ' '; + break; + case '\\': + chars[j++] = '\\'; + break; + case '\'': + chars[j++] = '\''; + break; + case '"': + chars[j++] = '"'; + break; + } + i += 2; + } else { + chars[j++] = s.charAt(i++); + } + } + return Arrays.copyOf(chars, j); + } + + private static RegularExpression getCharsRegex(String s) { + int[] chars = getChars(s); + if (chars.length == 1) { + return Char.from(chars[0]); + } + return Seq.from(chars); + } + + private static int getRangeChar(String s) { + switch (s) { + case "\\n": + return '\n'; + case "\\r": + return '\r'; + case "\\t": + return '\t'; + case "\\f": + return '\f'; + case "\\'": + return '\''; + case "\\\"": + return '\"'; + case "\\ ": + return ' '; + case "\\[": + return '['; + case "\\]": + return ']'; + case "\\-": + return '-'; + } + return s.charAt(0); + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/iggy/IggyParserUtils.java b/benchmarks/src/main/kotlin/org/iguana/iggy/IggyParserUtils.java new file mode 100644 index 000000000..0905ee902 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/iggy/IggyParserUtils.java @@ -0,0 +1,58 @@ +package org.iguana.iggy; + +import org.iguana.grammar.Grammar; +import org.iguana.grammar.symbol.Start; +import org.iguana.iggy.gen.IggyGrammar; +import org.iguana.iggy.gen.IggyParser; +import org.iguana.parser.IguanaParser; +import org.iguana.parser.ParseErrorException; +import org.iguana.parsetree.ParseTreeNode; +import org.iguana.utils.input.Input; + +import java.io.File; +import java.io.IOException; +import java.util.List; + +public class IggyParserUtils { + + // Creates a Grammar form the provided .iggy file + public static Grammar fromIggyGrammarPath(String path) { + Input input; + try { + input = Input.fromFile(new File(path)); + } catch (IOException e) { + throw new RuntimeException(e); + } + return createGrammar(input); + } + + // Creates a Grammar form the provided grammar in string form + public static Grammar fromIggyGrammar(String content) { + Input input = Input.fromString(content); + return createGrammar(input); + } + + private static Grammar createGrammar(Input input) { + Start start = IggyGrammar.getGrammar().getStartSymbols().get(0); + IguanaParser parser = IggyParser.getInstance(); + try { + parser.parse(input, start); + } catch (ParseErrorException e) { + System.out.println(parser.getParseError()); + throw new RuntimeException(parser.getParseError().toString()); + } + + ParseTreeNode parseTree = parser.getParseTree(); + Object result = parseTree.accept(new IggyParseTreeToGrammarVisitor()); + if (result instanceof List) { // When there is a start symbol + List nodes = (List) result; + if (nodes.size() == 1) { // No layout definition + return (Grammar) nodes.get(0); + } else { // Layout Grammar Layout + return (Grammar) nodes.get(1); + } + } else { + return (Grammar) result; + } + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/iggy/IggyRegexCategories.java b/benchmarks/src/main/kotlin/org/iguana/iggy/IggyRegexCategories.java new file mode 100644 index 000000000..6199dc1c9 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/iggy/IggyRegexCategories.java @@ -0,0 +1,50 @@ +package org.iguana.iggy; + +import org.iguana.grammar.runtime.RuntimeGrammar; +import org.iguana.iggy.gen.IggyGrammar; +import org.iguana.regex.Alt; +import org.iguana.regex.RegularExpression; + +import java.util.LinkedHashMap; +import java.util.Map; + +public class IggyRegexCategories { + + private static final Map regularExpressionCategories = new LinkedHashMap<>(); + + static { + RuntimeGrammar runtimeGrammar = IggyGrammar.getGrammar().toRuntimeGrammar(); + + // TODO: this is a hack, find a better solution to mark keywords + RegularExpression keywords = runtimeGrammar.getRegularExpressionDefinitions().get("Keywords"); + if (keywords != null) { + Alt alt = (Alt) keywords; + for (RegularExpression regex : alt.getSymbols()) { + regularExpressionCategories.put(regex, "Keyword"); + } + } + + for (Map.Entry entry : runtimeGrammar.getLiterals().entrySet()) { + if (!regularExpressionCategories.containsKey(entry.getValue())) { + regularExpressionCategories.put(entry.getValue(), entry.getKey()); + } + } + + for (Map.Entry entry : runtimeGrammar.getRegularExpressionDefinitions().entrySet()) { + RegularExpression regularExpression = entry.getValue(); + String category = entry.getKey(); + + if (!category.equals("Keywords")) { + regularExpressionCategories.put(regularExpression, category); + } + } + } + + public static Map getCategories() { + return regularExpressionCategories; + } + + public static String getCategory(RegularExpression regex) { + return regularExpressionCategories.get(regex); + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/iggy/gen/IggyGrammar.java b/benchmarks/src/main/kotlin/org/iguana/iggy/gen/IggyGrammar.java new file mode 100644 index 000000000..c202904d8 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/iggy/gen/IggyGrammar.java @@ -0,0 +1,33 @@ +// This file has been generated, do not directly edit this file! + +package org.iguana.iggy.gen; + +import org.iguana.grammar.Grammar; +import org.iguana.util.serialization.JsonSerializer; + +import java.io.IOException; +import java.io.InputStream; + +public class IggyGrammar { + + private static final String grammarName = "iggy"; + + private static Grammar grammar; + + public static Grammar getGrammar() { + if (grammar == null) { + grammar = loadGrammar(); + } + return grammar; + } + + private static Grammar loadGrammar() { + String grammarJsonFile = grammarName + ".json"; + try (InputStream in = IggyParser.class.getResourceAsStream("/" + grammarJsonFile)) { + if (in == null) throw new RuntimeException("Grammar json file " + grammarJsonFile + " is not found."); + return JsonSerializer.deserialize(in, Grammar.class); + } catch (IOException e) { + throw new RuntimeException(e); + } + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/iggy/gen/IggyParseTree.java b/benchmarks/src/main/kotlin/org/iguana/iggy/gen/IggyParseTree.java new file mode 100644 index 000000000..69dd6c039 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/iggy/gen/IggyParseTree.java @@ -0,0 +1,2036 @@ +// This file has been generated, do not directly edit this file! + +package org.iguana.iggy.gen; + +import org.iguana.grammar.runtime.RuntimeRule; +import org.iguana.parsetree.NonterminalNode; +import org.iguana.parsetree.ParseTreeNode; +import org.iguana.parsetree.ParseTreeVisitor; +import org.iguana.parsetree.TerminalNode; + +import java.util.List; + +import static org.iguana.parsetree.MetaSymbolNode.OptionNode; +import static org.iguana.parsetree.MetaSymbolNode.PlusNode; +import static org.iguana.parsetree.MetaSymbolNode.StarNode; + +public class IggyParseTree { + // Grammar = 'grammar'? name:Identifier? defs:(Rule | TopLevelVar)+ + public static class Grammar extends NonterminalNode { + public Grammar(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + public OptionNode name() { + return (OptionNode) childAt(1); + } + + public PlusNode defs() { + return (PlusNode) childAt(2); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visitGrammar(this); + } + return visitor.visitNonterminalNode(this); + } + } + + // TopLevelVar = 'global'? 'var' id:Identifier '=' exp:exp:Expression {env = put(env,id.yield)} + public static class TopLevelVar extends NonterminalNode { + public TopLevelVar(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + public Identifier id() { + return (Identifier) childAt(2); + } + + public Expression exp() { + return (Expression) childAt(4); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visitTopLevelVar(this); + } + return visitor.visitNonterminalNode(this); + } + } + + public abstract static class Rule extends NonterminalNode { + public Rule(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + } + + // Rule = modifier:('start' | 'layout' | 'lexical')? name:Name params:Parameters? '=' body:Body + public static class ContextFreeRule extends Rule { + public ContextFreeRule(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + public OptionNode modifier() { + return (OptionNode) childAt(0); + } + + public Name name() { + return (Name) childAt(1); + } + + public OptionNode params() { + return (OptionNode) childAt(2); + } + + public Body body() { + return (Body) childAt(4); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visitContextFreeRule(this); + } + return visitor.visitNonterminalNode(this); + } + } + + // Rule = modifier:'layout'? 'regex' name:Name '=' body:RegexBody + public static class RegexRule extends Rule { + public RegexRule(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + public OptionNode modifier() { + return (OptionNode) childAt(0); + } + + public Name name() { + return (Name) childAt(2); + } + + public RegexBody body() { + return (RegexBody) childAt(4); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visitRegexRule(this); + } + return visitor.visitNonterminalNode(this); + } + } + + // Parameters = '(' {id:Identifier {env = put(env,id.yield)} ','}* ')' + public static class Parameters extends NonterminalNode { + public Parameters(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visitParameters(this); + } + return visitor.visitNonterminalNode(this); + } + } + + // RegexBody = {Regex+ '|'}* + public static class RegexBody extends NonterminalNode { + public RegexBody(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visitRegexBody(this); + } + return visitor.visitNonterminalNode(this); + } + } + + // Body = {PriorityLevels '>'}+ + public static class Body extends NonterminalNode { + public Body(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visitBody(this); + } + return visitor.visitNonterminalNode(this); + } + } + + // PriorityLevels = {Alternative '|'}+ + public static class PriorityLevels extends NonterminalNode { + public PriorityLevels(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visitPriorityLevels(this); + } + return visitor.visitNonterminalNode(this); + } + } + + public abstract static class Alternative extends NonterminalNode { + public Alternative(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + } + + // Alternative = seq:Sequence + public static class SequenceAlternative extends Alternative { + public SequenceAlternative(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + public Sequence seq() { + return (Sequence) childAt(0); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visitSequenceAlternative(this); + } + return visitor.visitNonterminalNode(this); + } + } + + // Alternative = assoc:Associativity '(' seqs:{Sequence '|'}+ ')' + public static class AssociativityAlternative extends Alternative { + public AssociativityAlternative(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + public TerminalNode assoc() { + return (TerminalNode) childAt(0); + } + + public PlusNode seqs() { + return (PlusNode) childAt(2); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visitAssociativityAlternative(this); + } + return visitor.visitNonterminalNode(this); + } + } + + public abstract static class Sequence extends NonterminalNode { + public Sequence(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + } + + // Sequence = assoc:Associativity? cond:Condition? first:Symbol rest:Symbol+ ret:ReturnExpression? label:Label? + public static class MoreThanOneElemSequence extends Sequence { + public MoreThanOneElemSequence(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + public OptionNode assoc() { + return (OptionNode) childAt(0); + } + + public OptionNode cond() { + return (OptionNode) childAt(1); + } + + public Symbol first() { + return (Symbol) childAt(2); + } + + public PlusNode rest() { + return (PlusNode) childAt(3); + } + + public OptionNode ret() { + return (OptionNode) childAt(4); + } + + public OptionNode label() { + return (OptionNode) childAt(5); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visitMoreThanOneElemSequence(this); + } + return visitor.visitNonterminalNode(this); + } + } + + // Sequence = cond:Condition? sym:Symbol ret:ReturnExpression? label:Label? + public static class SingleElemSequence extends Sequence { + public SingleElemSequence(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + public OptionNode cond() { + return (OptionNode) childAt(0); + } + + public Symbol sym() { + return (Symbol) childAt(1); + } + + public OptionNode ret() { + return (OptionNode) childAt(2); + } + + public OptionNode label() { + return (OptionNode) childAt(3); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visitSingleElemSequence(this); + } + return visitor.visitNonterminalNode(this); + } + } + + // Sequence = label:Label? + public static class EmptySequence extends Sequence { + public EmptySequence(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + public OptionNode label() { + return (OptionNode) childAt(0); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visitEmptySequence(this); + } + return visitor.visitNonterminalNode(this); + } + } + + // Condition = '{' {Expression ','}* '}' '?' + public static class Condition extends NonterminalNode { + public Condition(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visitCondition(this); + } + return visitor.visitNonterminalNode(this); + } + } + + public abstract static class Symbol extends NonterminalNode { + public Symbol(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + } + + // Symbol = id:Identifier args:Arguments + public static class CallSymbol extends Symbol { + public CallSymbol(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + public Identifier id() { + return (Identifier) childAt(0); + } + + public Arguments args() { + return (Arguments) childAt(1); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visitCallSymbol(this); + } + return visitor.visitNonterminalNode(this); + } + } + + // Symbol = 'offside' sym:Symbol + public static class OffsideSymbol extends Symbol { + public OffsideSymbol(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + public Symbol sym() { + return (Symbol) childAt(1); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visitOffsideSymbol(this); + } + return visitor.visitNonterminalNode(this); + } + } + + // Symbol = sym:Symbol '*' + public static class StarSymbol extends Symbol { + public StarSymbol(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + public Symbol sym() { + return (Symbol) childAt(0); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visitStarSymbol(this); + } + return visitor.visitNonterminalNode(this); + } + } + + // Symbol = sym:Symbol '+' + public static class PlusSymbol extends Symbol { + public PlusSymbol(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + public Symbol sym() { + return (Symbol) childAt(0); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visitPlusSymbol(this); + } + return visitor.visitNonterminalNode(this); + } + } + + // Symbol = sym:Symbol '?' + public static class OptionSymbol extends Symbol { + public OptionSymbol(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + public Symbol sym() { + return (Symbol) childAt(0); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visitOptionSymbol(this); + } + return visitor.visitNonterminalNode(this); + } + } + + // Symbol = '(' syms:Symbol+ ')' + public static class SequenceSymbol extends Symbol { + public SequenceSymbol(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + public PlusNode syms() { + return (PlusNode) childAt(1); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visitSequenceSymbol(this); + } + return visitor.visitNonterminalNode(this); + } + } + + // Symbol = '(' first:Symbol+ rest:('|' Symbol+)+ ')' + public static class AlternationSymbol extends Symbol { + public AlternationSymbol(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + public PlusNode first() { + return (PlusNode) childAt(1); + } + + public PlusNode rest() { + return (PlusNode) childAt(2); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visitAlternationSymbol(this); + } + return visitor.visitNonterminalNode(this); + } + } + + // Symbol = 'align' sym:Symbol + public static class AlignSymbol extends Symbol { + public AlignSymbol(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + public Symbol sym() { + return (Symbol) childAt(1); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visitAlignSymbol(this); + } + return visitor.visitNonterminalNode(this); + } + } + + // Symbol = 'ignore' sym:Symbol + public static class IgnoreSymbol extends Symbol { + public IgnoreSymbol(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + public Symbol sym() { + return (Symbol) childAt(1); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visitIgnoreSymbol(this); + } + return visitor.visitNonterminalNode(this); + } + } + + // Symbol = id:Identifier ':' sym:Symbol + public static class LabeledSymbol extends Symbol { + public LabeledSymbol(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + public Identifier id() { + return (Identifier) childAt(0); + } + + public Symbol sym() { + return (Symbol) childAt(2); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visitLabeledSymbol(this); + } + return visitor.visitNonterminalNode(this); + } + } + + // Symbol = sym:Symbol stmts:Statement+ + public static class StatementSymbol extends Symbol { + public StatementSymbol(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + public Symbol sym() { + return (Symbol) childAt(0); + } + + public PlusNode stmts() { + return (PlusNode) childAt(1); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visitStatementSymbol(this); + } + return visitor.visitNonterminalNode(this); + } + } + + // Symbol = sym:Symbol cond:Condition + public static class PostConditionSymbol extends Symbol { + public PostConditionSymbol(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + public Symbol sym() { + return (Symbol) childAt(0); + } + + public Condition cond() { + return (Condition) childAt(1); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visitPostConditionSymbol(this); + } + return visitor.visitNonterminalNode(this); + } + } + + // Symbol = reg:Regex '<<' sym:Symbol + public static class PrecedeSymbol extends Symbol { + public PrecedeSymbol(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + public Regex reg() { + return (Regex) childAt(0); + } + + public Symbol sym() { + return (Symbol) childAt(2); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visitPrecedeSymbol(this); + } + return visitor.visitNonterminalNode(this); + } + } + + // Symbol = reg:Regex '!<<' sym:Symbol + public static class NotPrecedeSymbol extends Symbol { + public NotPrecedeSymbol(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + public Regex reg() { + return (Regex) childAt(0); + } + + public Symbol sym() { + return (Symbol) childAt(2); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visitNotPrecedeSymbol(this); + } + return visitor.visitNonterminalNode(this); + } + } + + // Symbol = '^' sym:Symbol + public static class StartOfLineSymbol extends Symbol { + public StartOfLineSymbol(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + public Symbol sym() { + return (Symbol) childAt(1); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visitStartOfLineSymbol(this); + } + return visitor.visitNonterminalNode(this); + } + } + + // Symbol = sym:Symbol '>>' reg:Regex + public static class FollowSymbol extends Symbol { + public FollowSymbol(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + public Symbol sym() { + return (Symbol) childAt(0); + } + + public Regex reg() { + return (Regex) childAt(2); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visitFollowSymbol(this); + } + return visitor.visitNonterminalNode(this); + } + } + + // Symbol = sym:Symbol '!>>' reg:Regex + public static class NotFollowSymbol extends Symbol { + public NotFollowSymbol(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + public Symbol sym() { + return (Symbol) childAt(0); + } + + public Regex reg() { + return (Regex) childAt(2); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visitNotFollowSymbol(this); + } + return visitor.visitNonterminalNode(this); + } + } + + // Symbol = sym:Symbol '\' reg:Regex + public static class ExcludeSymbol extends Symbol { + public ExcludeSymbol(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + public Symbol sym() { + return (Symbol) childAt(0); + } + + public Regex reg() { + return (Regex) childAt(2); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visitExcludeSymbol(this); + } + return visitor.visitNonterminalNode(this); + } + } + + // Symbol = sym:Symbol '!' id:Identifier + public static class ExceptSymbol extends Symbol { + public ExceptSymbol(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + public Symbol sym() { + return (Symbol) childAt(0); + } + + public Identifier id() { + return (Identifier) childAt(2); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visitExceptSymbol(this); + } + return visitor.visitNonterminalNode(this); + } + } + + // Symbol = sym:Symbol '$' + public static class EndOfLineSymbol extends Symbol { + public EndOfLineSymbol(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + public Symbol sym() { + return (Symbol) childAt(0); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visitEndOfLineSymbol(this); + } + return visitor.visitNonterminalNode(this); + } + } + + // Symbol = sym:Symbol '$$' + public static class EndOfFileSymbol extends Symbol { + public EndOfFileSymbol(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + public Symbol sym() { + return (Symbol) childAt(0); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visitEndOfFileSymbol(this); + } + return visitor.visitNonterminalNode(this); + } + } + + // Symbol = 'if' exp:Expression thenPart:Symbol 'else' elsePart:Symbol + public static class IfThenElseSymbol extends Symbol { + public IfThenElseSymbol(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + public Expression exp() { + return (Expression) childAt(1); + } + + public Symbol thenPart() { + return (Symbol) childAt(2); + } + + public Symbol elsePart() { + return (Symbol) childAt(4); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visitIfThenElseSymbol(this); + } + return visitor.visitNonterminalNode(this); + } + } + + // Symbol = id:Identifier + public static class IdentifierSymbol extends Symbol { + public IdentifierSymbol(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + public Identifier id() { + return (Identifier) childAt(0); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visitIdentifierSymbol(this); + } + return visitor.visitNonterminalNode(this); + } + } + + // Symbol = string:String + public static class StringSymbol extends Symbol { + public StringSymbol(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + public TerminalNode string() { + return (TerminalNode) childAt(0); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visitStringSymbol(this); + } + return visitor.visitNonterminalNode(this); + } + } + + // Symbol = charClass:CharClass + public static class CharClassSymbol extends Symbol { + public CharClassSymbol(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + public CharClass charClass() { + return (CharClass) childAt(0); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visitCharClassSymbol(this); + } + return visitor.visitNonterminalNode(this); + } + } + + // Symbol = '{' sym:Symbol sep:Symbol+ '}' '*' + public static class StarSepSymbol extends Symbol { + public StarSepSymbol(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + public Symbol sym() { + return (Symbol) childAt(1); + } + + public PlusNode sep() { + return (PlusNode) childAt(2); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visitStarSepSymbol(this); + } + return visitor.visitNonterminalNode(this); + } + } + + // Symbol = '{' sym:Symbol sep:Symbol+ '}' '+' + public static class PlusSepSymbol extends Symbol { + public PlusSepSymbol(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + public Symbol sym() { + return (Symbol) childAt(1); + } + + public PlusNode sep() { + return (PlusNode) childAt(2); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visitPlusSepSymbol(this); + } + return visitor.visitNonterminalNode(this); + } + } + + // Symbol = 'error' + public static class ErrorSymbol extends Symbol { + public ErrorSymbol(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visitErrorSymbol(this); + } + return visitor.visitNonterminalNode(this); + } + } + + // Arguments = '(' {Expression ','}* ')' + public static class Arguments extends NonterminalNode { + public Arguments(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visitArguments(this); + } + return visitor.visitNonterminalNode(this); + } + } + + public abstract static class Statement extends NonterminalNode { + public Statement(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + } + + // Statement = fun:FunName args:Arguments ';'? + public static class CallStatement extends Statement { + public CallStatement(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + public FunName fun() { + return (FunName) childAt(0); + } + + public Arguments args() { + return (Arguments) childAt(1); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visitCallStatement(this); + } + return visitor.visitNonterminalNode(this); + } + } + + // Statement = bindings:Binding ';'? + public static class BindingStatement extends Statement { + public BindingStatement(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + public Binding bindings() { + return (Binding) childAt(0); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visitBindingStatement(this); + } + return visitor.visitNonterminalNode(this); + } + } + + public abstract static class Binding extends NonterminalNode { + public Binding(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + } + + // Binding = varName:VarName '=' exp:Expression + public static class AssignBinding extends Binding { + public AssignBinding(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + public VarName varName() { + return (VarName) childAt(0); + } + + public Expression exp() { + return (Expression) childAt(2); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visitAssignBinding(this); + } + return visitor.visitNonterminalNode(this); + } + } + + // Binding = 'var' decls:{(id:Name {env = put(env,id.yield)} '=' Expression) ','}+ + public static class DeclareBinding extends Binding { + public DeclareBinding(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + public PlusNode decls() { + return (PlusNode) childAt(1); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visitDeclareBinding(this); + } + return visitor.visitNonterminalNode(this); + } + } + + public abstract static class Regex extends NonterminalNode { + public Regex(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + } + + // Regex = reg:Regex '*' + public static class StarRegex extends Regex { + public StarRegex(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + public Regex reg() { + return (Regex) childAt(0); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visitStarRegex(this); + } + return visitor.visitNonterminalNode(this); + } + } + + // Regex = reg:Regex '+' + public static class PlusRegex extends Regex { + public PlusRegex(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + public Regex reg() { + return (Regex) childAt(0); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visitPlusRegex(this); + } + return visitor.visitNonterminalNode(this); + } + } + + // Regex = reg:Regex '?' + public static class OptionRegex extends Regex { + public OptionRegex(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + public Regex reg() { + return (Regex) childAt(0); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visitOptionRegex(this); + } + return visitor.visitNonterminalNode(this); + } + } + + // Regex = '(' reg:Regex ')' + public static class BracketRegex extends Regex { + public BracketRegex(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + public Regex reg() { + return (Regex) childAt(1); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visitBracketRegex(this); + } + return visitor.visitNonterminalNode(this); + } + } + + // Regex = '(' first:Regex rest:Regex+ ')' + public static class SequenceRegex extends Regex { + public SequenceRegex(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + public Regex first() { + return (Regex) childAt(1); + } + + public PlusNode rest() { + return (PlusNode) childAt(2); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visitSequenceRegex(this); + } + return visitor.visitNonterminalNode(this); + } + } + + // Regex = '(' first:Regex+ '|' rest:{Regex+ '|'}+ ')' + public static class AlternationRegex extends Regex { + public AlternationRegex(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + public PlusNode first() { + return (PlusNode) childAt(1); + } + + public PlusNode rest() { + return (PlusNode) childAt(3); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visitAlternationRegex(this); + } + return visitor.visitNonterminalNode(this); + } + } + + // Regex = name:Name + public static class NontRegex extends Regex { + public NontRegex(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + public Name name() { + return (Name) childAt(0); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visitNontRegex(this); + } + return visitor.visitNonterminalNode(this); + } + } + + // Regex = charClass:CharClass + public static class CharClassRegex extends Regex { + public CharClassRegex(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + public CharClass charClass() { + return (CharClass) childAt(0); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visitCharClassRegex(this); + } + return visitor.visitNonterminalNode(this); + } + } + + // Regex = string:String + public static class StringRegex extends Regex { + public StringRegex(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + public TerminalNode string() { + return (TerminalNode) childAt(0); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visitStringRegex(this); + } + return visitor.visitNonterminalNode(this); + } + } + + public abstract static class CharClass extends NonterminalNode { + public CharClass(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + } + + // CharClass = '[' ranges:Range* ']' + public static class CharsCharClass extends CharClass { + public CharsCharClass(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + public StarNode ranges() { + return (StarNode) childAt(1); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visitCharsCharClass(this); + } + return visitor.visitNonterminalNode(this); + } + } + + // CharClass = '!' '[' ranges:Range* ']' + public static class NotCharsCharClass extends CharClass { + public NotCharsCharClass(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + public StarNode ranges() { + return (StarNode) childAt(2); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visitNotCharsCharClass(this); + } + return visitor.visitNonterminalNode(this); + } + } + + public abstract static class Range extends NonterminalNode { + public Range(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + } + + // Range = first:RangeChar '-' second:RangeChar + public static class RangeRange extends Range { + public RangeRange(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + public TerminalNode first() { + return (TerminalNode) childAt(0); + } + + public TerminalNode second() { + return (TerminalNode) childAt(2); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visitRangeRange(this); + } + return visitor.visitNonterminalNode(this); + } + } + + // Range = range:RangeChar + public static class CharacterRange extends Range { + public CharacterRange(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + public TerminalNode range() { + return (TerminalNode) childAt(0); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visitCharacterRange(this); + } + return visitor.visitNonterminalNode(this); + } + } + + public abstract static class Expression extends NonterminalNode { + public Expression(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + } + + // Expression = fun:FunName args:Arguments + public static class CallExpression extends Expression { + public CallExpression(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + public FunName fun() { + return (FunName) childAt(0); + } + + public Arguments args() { + return (Arguments) childAt(1); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visitCallExpression(this); + } + return visitor.visitNonterminalNode(this); + } + } + + // Expression = '!' exp:Expression + public static class NotExpression extends Expression { + public NotExpression(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + public Expression exp() { + return (Expression) childAt(1); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visitNotExpression(this); + } + return visitor.visitNonterminalNode(this); + } + } + + // Expression = lhs:Expression '*' rhs:Expression + public static class MultiplicationExpression extends Expression { + public MultiplicationExpression(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + public Expression lhs() { + return (Expression) childAt(0); + } + + public Expression rhs() { + return (Expression) childAt(2); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visitMultiplicationExpression(this); + } + return visitor.visitNonterminalNode(this); + } + } + + // Expression = lhs:Expression '/' rhs:Expression + public static class DivisionExpression extends Expression { + public DivisionExpression(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + public Expression lhs() { + return (Expression) childAt(0); + } + + public Expression rhs() { + return (Expression) childAt(2); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visitDivisionExpression(this); + } + return visitor.visitNonterminalNode(this); + } + } + + // Expression = lhs:Expression '+' rhs:Expression + public static class AdditionExpression extends Expression { + public AdditionExpression(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + public Expression lhs() { + return (Expression) childAt(0); + } + + public Expression rhs() { + return (Expression) childAt(2); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visitAdditionExpression(this); + } + return visitor.visitNonterminalNode(this); + } + } + + // Expression = lhs:Expression '-' rhs:Expression + public static class SubtractionExpression extends Expression { + public SubtractionExpression(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + public Expression lhs() { + return (Expression) childAt(0); + } + + public Expression rhs() { + return (Expression) childAt(2); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visitSubtractionExpression(this); + } + return visitor.visitNonterminalNode(this); + } + } + + // Expression = lhs:Expression '>=' rhs:Expression + public static class GreaterEqExpression extends Expression { + public GreaterEqExpression(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + public Expression lhs() { + return (Expression) childAt(0); + } + + public Expression rhs() { + return (Expression) childAt(2); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visitGreaterEqExpression(this); + } + return visitor.visitNonterminalNode(this); + } + } + + // Expression = lhs:Expression '<=' rhs:Expression + public static class LessEqExpression extends Expression { + public LessEqExpression(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + public Expression lhs() { + return (Expression) childAt(0); + } + + public Expression rhs() { + return (Expression) childAt(2); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visitLessEqExpression(this); + } + return visitor.visitNonterminalNode(this); + } + } + + // Expression = lhs:Expression '>' rhs:Expression + public static class GreaterExpression extends Expression { + public GreaterExpression(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + public Expression lhs() { + return (Expression) childAt(0); + } + + public Expression rhs() { + return (Expression) childAt(2); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visitGreaterExpression(this); + } + return visitor.visitNonterminalNode(this); + } + } + + // Expression = lhs:Expression '<' rhs:Expression + public static class LessExpression extends Expression { + public LessExpression(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + public Expression lhs() { + return (Expression) childAt(0); + } + + public Expression rhs() { + return (Expression) childAt(2); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visitLessExpression(this); + } + return visitor.visitNonterminalNode(this); + } + } + + // Expression = lhs:Expression '==' rhs:Expression + public static class EqualExpression extends Expression { + public EqualExpression(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + public Expression lhs() { + return (Expression) childAt(0); + } + + public Expression rhs() { + return (Expression) childAt(2); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visitEqualExpression(this); + } + return visitor.visitNonterminalNode(this); + } + } + + // Expression = lhs:Expression '!=' rhs:Expression + public static class NotEqualExpression extends Expression { + public NotEqualExpression(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + public Expression lhs() { + return (Expression) childAt(0); + } + + public Expression rhs() { + return (Expression) childAt(2); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visitNotEqualExpression(this); + } + return visitor.visitNonterminalNode(this); + } + } + + // Expression = lhs:Expression '&&' rhs:Expression + public static class AndExpression extends Expression { + public AndExpression(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + public Expression lhs() { + return (Expression) childAt(0); + } + + public Expression rhs() { + return (Expression) childAt(2); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visitAndExpression(this); + } + return visitor.visitNonterminalNode(this); + } + } + + // Expression = lhs:Expression '||' rhs:Expression + public static class OrExpression extends Expression { + public OrExpression(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + public Expression lhs() { + return (Expression) childAt(0); + } + + public Expression rhs() { + return (Expression) childAt(2); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visitOrExpression(this); + } + return visitor.visitNonterminalNode(this); + } + } + + // Expression = id:Identifier '.l' + public static class LExtentExpression extends Expression { + public LExtentExpression(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + public Identifier id() { + return (Identifier) childAt(0); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visitLExtentExpression(this); + } + return visitor.visitNonterminalNode(this); + } + } + + // Expression = id:Identifier '.r' + public static class RExtentExpression extends Expression { + public RExtentExpression(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + public Identifier id() { + return (Identifier) childAt(0); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visitRExtentExpression(this); + } + return visitor.visitNonterminalNode(this); + } + } + + // Expression = id:Identifier '.yield' + public static class YieldExpression extends Expression { + public YieldExpression(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + public Identifier id() { + return (Identifier) childAt(0); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visitYieldExpression(this); + } + return visitor.visitNonterminalNode(this); + } + } + + // Expression = id:Identifier '.val' + public static class ValExpression extends Expression { + public ValExpression(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + public Identifier id() { + return (Identifier) childAt(0); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visitValExpression(this); + } + return visitor.visitNonterminalNode(this); + } + } + + // Expression = varName:VarName + public static class NameExpression extends Expression { + public NameExpression(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + public VarName varName() { + return (VarName) childAt(0); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visitNameExpression(this); + } + return visitor.visitNonterminalNode(this); + } + } + + // Expression = number:Number + public static class NumberExpression extends Expression { + public NumberExpression(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + public TerminalNode number() { + return (TerminalNode) childAt(0); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visitNumberExpression(this); + } + return visitor.visitNonterminalNode(this); + } + } + + // Expression = '(' exp:Expression ')' + public static class BracketExpression extends Expression { + public BracketExpression(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + public Expression exp() { + return (Expression) childAt(1); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visitBracketExpression(this); + } + return visitor.visitNonterminalNode(this); + } + } + + // ReturnExpression = '{' exp:Expression '}' + public static class ReturnExpression extends NonterminalNode { + public ReturnExpression(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + public Expression exp() { + return (Expression) childAt(1); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visitReturnExpression(this); + } + return visitor.visitNonterminalNode(this); + } + } + + // VarName = id:Identifier + public static class VarName extends NonterminalNode { + public VarName(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + public Identifier id() { + return (Identifier) childAt(0); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visitVarName(this); + } + return visitor.visitNonterminalNode(this); + } + } + + // Name = id:Identifier + public static class Name extends NonterminalNode { + public Name(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + public Identifier id() { + return (Identifier) childAt(0); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visitName(this); + } + return visitor.visitNonterminalNode(this); + } + } + + public abstract static class FunName extends NonterminalNode { + public FunName(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + } + + // FunName = 'println' + public static class PrintlnFunName extends FunName { + public PrintlnFunName(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visitPrintlnFunName(this); + } + return visitor.visitNonterminalNode(this); + } + } + + // FunName = 'indent' + public static class IndentFunName extends FunName { + public IndentFunName(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visitIndentFunName(this); + } + return visitor.visitNonterminalNode(this); + } + } + + // FunName = 'assert' + public static class AssertFunName extends FunName { + public AssertFunName(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visitAssertFunName(this); + } + return visitor.visitNonterminalNode(this); + } + } + + // FunName = 'set' + public static class SetFunName extends FunName { + public SetFunName(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visitSetFunName(this); + } + return visitor.visitNonterminalNode(this); + } + } + + // FunName = 'contains' + public static class ContainsFunName extends FunName { + public ContainsFunName(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visitContainsFunName(this); + } + return visitor.visitNonterminalNode(this); + } + } + + // FunName = 'put' + public static class PutFunName extends FunName { + public PutFunName(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visitPutFunName(this); + } + return visitor.visitNonterminalNode(this); + } + } + + // Identifier = LetterOrDigits + public static class Identifier extends NonterminalNode { + public Identifier(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visitIdentifier(this); + } + return visitor.visitNonterminalNode(this); + } + } + + // Label = '%' id:Identifier + public static class Label extends NonterminalNode { + public Label(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + public Identifier id() { + return (Identifier) childAt(1); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visitLabel(this); + } + return visitor.visitNonterminalNode(this); + } + } + + // Layout = (WhiteSpace | SingleLineComment | MultiLineComment)* + public static class Layout extends NonterminalNode { + public Layout(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visitLayout(this); + } + return visitor.visitNonterminalNode(this); + } + } + + // $_Symbol = child:Symbol + public static class $_Symbol extends NonterminalNode { + public $_Symbol(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + public Symbol child() { + return (Symbol) childAt(0); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visit$_Symbol(this); + } + return visitor.visitNonterminalNode(this); + } + } + + // $_Expression = child:Expression + public static class $_Expression extends NonterminalNode { + public $_Expression(RuntimeRule rule, List children, int start, int end) { + super(rule, children, start, end); + } + + public Expression child() { + return (Expression) childAt(0); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof IggyParseTreeVisitor) { + return ((IggyParseTreeVisitor) visitor).visit$_Expression(this); + } + return visitor.visitNonterminalNode(this); + } + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/iggy/gen/IggyParseTreeBuilder.java b/benchmarks/src/main/kotlin/org/iguana/iggy/gen/IggyParseTreeBuilder.java new file mode 100644 index 000000000..060e72c4a --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/iggy/gen/IggyParseTreeBuilder.java @@ -0,0 +1,278 @@ +// This file has been generated, do not directly edit this file! + +package org.iguana.iggy.gen; + +import org.iguana.grammar.runtime.RuntimeRule; +import org.iguana.parsetree.DefaultParseTreeBuilder; +import org.iguana.parsetree.NonterminalNode; +import org.iguana.parsetree.ParseTreeNode; +import org.iguana.utils.input.Input; + +import java.util.List; + +public class IggyParseTreeBuilder extends DefaultParseTreeBuilder { + + public IggyParseTreeBuilder(Input input) { + super(input); + } + + @Override + public NonterminalNode nonterminalNode( + RuntimeRule rule, + List children, + int leftExtent, + int rightExtent) { + java.lang.String name = rule.getHead().getName(); + java.lang.String label = rule.getLabel(); + + switch (name) { + case "Grammar": + return new IggyParseTree.Grammar(rule, children, leftExtent, rightExtent); + case "TopLevelVar": + return new IggyParseTree.TopLevelVar(rule, children, leftExtent, rightExtent); + case "Rule": + switch (label) { + case "ContextFree": + return new IggyParseTree.ContextFreeRule(rule, children, leftExtent, rightExtent); + case "Regex": + return new IggyParseTree.RegexRule(rule, children, leftExtent, rightExtent); + default: + throw new RuntimeException("Unexpected label:" + label); + } + case "Parameters": + return new IggyParseTree.Parameters(rule, children, leftExtent, rightExtent); + case "RegexBody": + return new IggyParseTree.RegexBody(rule, children, leftExtent, rightExtent); + case "Body": + return new IggyParseTree.Body(rule, children, leftExtent, rightExtent); + case "PriorityLevels": + return new IggyParseTree.PriorityLevels(rule, children, leftExtent, rightExtent); + case "Alternative": + switch (label) { + case "Sequence": + return new IggyParseTree.SequenceAlternative(rule, children, leftExtent, rightExtent); + case "Associativity": + return new IggyParseTree.AssociativityAlternative(rule, children, leftExtent, rightExtent); + default: + throw new RuntimeException("Unexpected label:" + label); + } + case "Sequence": + switch (label) { + case "MoreThanOneElem": + return new IggyParseTree.MoreThanOneElemSequence(rule, children, leftExtent, rightExtent); + case "SingleElem": + return new IggyParseTree.SingleElemSequence(rule, children, leftExtent, rightExtent); + case "Empty": + return new IggyParseTree.EmptySequence(rule, children, leftExtent, rightExtent); + default: + throw new RuntimeException("Unexpected label:" + label); + } + case "Condition": + return new IggyParseTree.Condition(rule, children, leftExtent, rightExtent); + case "Symbol": + switch (label) { + case "Call": + return new IggyParseTree.CallSymbol(rule, children, leftExtent, rightExtent); + case "Offside": + return new IggyParseTree.OffsideSymbol(rule, children, leftExtent, rightExtent); + case "Star": + return new IggyParseTree.StarSymbol(rule, children, leftExtent, rightExtent); + case "Plus": + return new IggyParseTree.PlusSymbol(rule, children, leftExtent, rightExtent); + case "Option": + return new IggyParseTree.OptionSymbol(rule, children, leftExtent, rightExtent); + case "Sequence": + return new IggyParseTree.SequenceSymbol(rule, children, leftExtent, rightExtent); + case "Alternation": + return new IggyParseTree.AlternationSymbol(rule, children, leftExtent, rightExtent); + case "Align": + return new IggyParseTree.AlignSymbol(rule, children, leftExtent, rightExtent); + case "Ignore": + return new IggyParseTree.IgnoreSymbol(rule, children, leftExtent, rightExtent); + case "Labeled": + return new IggyParseTree.LabeledSymbol(rule, children, leftExtent, rightExtent); + case "Statement": + return new IggyParseTree.StatementSymbol(rule, children, leftExtent, rightExtent); + case "PostCondition": + return new IggyParseTree.PostConditionSymbol(rule, children, leftExtent, rightExtent); + case "Precede": + return new IggyParseTree.PrecedeSymbol(rule, children, leftExtent, rightExtent); + case "NotPrecede": + return new IggyParseTree.NotPrecedeSymbol(rule, children, leftExtent, rightExtent); + case "StartOfLine": + return new IggyParseTree.StartOfLineSymbol(rule, children, leftExtent, rightExtent); + case "Follow": + return new IggyParseTree.FollowSymbol(rule, children, leftExtent, rightExtent); + case "NotFollow": + return new IggyParseTree.NotFollowSymbol(rule, children, leftExtent, rightExtent); + case "Exclude": + return new IggyParseTree.ExcludeSymbol(rule, children, leftExtent, rightExtent); + case "Except": + return new IggyParseTree.ExceptSymbol(rule, children, leftExtent, rightExtent); + case "EndOfLine": + return new IggyParseTree.EndOfLineSymbol(rule, children, leftExtent, rightExtent); + case "EndOfFile": + return new IggyParseTree.EndOfFileSymbol(rule, children, leftExtent, rightExtent); + case "IfThenElse": + return new IggyParseTree.IfThenElseSymbol(rule, children, leftExtent, rightExtent); + case "Identifier": + return new IggyParseTree.IdentifierSymbol(rule, children, leftExtent, rightExtent); + case "String": + return new IggyParseTree.StringSymbol(rule, children, leftExtent, rightExtent); + case "CharClass": + return new IggyParseTree.CharClassSymbol(rule, children, leftExtent, rightExtent); + case "StarSep": + return new IggyParseTree.StarSepSymbol(rule, children, leftExtent, rightExtent); + case "PlusSep": + return new IggyParseTree.PlusSepSymbol(rule, children, leftExtent, rightExtent); + case "Error": + return new IggyParseTree.ErrorSymbol(rule, children, leftExtent, rightExtent); + default: + throw new RuntimeException("Unexpected label:" + label); + } + case "Arguments": + return new IggyParseTree.Arguments(rule, children, leftExtent, rightExtent); + case "Statement": + switch (label) { + case "Call": + return new IggyParseTree.CallStatement(rule, children, leftExtent, rightExtent); + case "Binding": + return new IggyParseTree.BindingStatement(rule, children, leftExtent, rightExtent); + default: + throw new RuntimeException("Unexpected label:" + label); + } + case "Binding": + switch (label) { + case "Assign": + return new IggyParseTree.AssignBinding(rule, children, leftExtent, rightExtent); + case "Declare": + return new IggyParseTree.DeclareBinding(rule, children, leftExtent, rightExtent); + default: + throw new RuntimeException("Unexpected label:" + label); + } + case "Regex": + switch (label) { + case "Star": + return new IggyParseTree.StarRegex(rule, children, leftExtent, rightExtent); + case "Plus": + return new IggyParseTree.PlusRegex(rule, children, leftExtent, rightExtent); + case "Option": + return new IggyParseTree.OptionRegex(rule, children, leftExtent, rightExtent); + case "Bracket": + return new IggyParseTree.BracketRegex(rule, children, leftExtent, rightExtent); + case "Sequence": + return new IggyParseTree.SequenceRegex(rule, children, leftExtent, rightExtent); + case "Alternation": + return new IggyParseTree.AlternationRegex(rule, children, leftExtent, rightExtent); + case "Nont": + return new IggyParseTree.NontRegex(rule, children, leftExtent, rightExtent); + case "CharClass": + return new IggyParseTree.CharClassRegex(rule, children, leftExtent, rightExtent); + case "String": + return new IggyParseTree.StringRegex(rule, children, leftExtent, rightExtent); + default: + throw new RuntimeException("Unexpected label:" + label); + } + case "CharClass": + switch (label) { + case "Chars": + return new IggyParseTree.CharsCharClass(rule, children, leftExtent, rightExtent); + case "NotChars": + return new IggyParseTree.NotCharsCharClass(rule, children, leftExtent, rightExtent); + default: + throw new RuntimeException("Unexpected label:" + label); + } + case "Range": + switch (label) { + case "Range": + return new IggyParseTree.RangeRange(rule, children, leftExtent, rightExtent); + case "Character": + return new IggyParseTree.CharacterRange(rule, children, leftExtent, rightExtent); + default: + throw new RuntimeException("Unexpected label:" + label); + } + case "Expression": + switch (label) { + case "Call": + return new IggyParseTree.CallExpression(rule, children, leftExtent, rightExtent); + case "Not": + return new IggyParseTree.NotExpression(rule, children, leftExtent, rightExtent); + case "Multiplication": + return new IggyParseTree.MultiplicationExpression(rule, children, leftExtent, rightExtent); + case "Division": + return new IggyParseTree.DivisionExpression(rule, children, leftExtent, rightExtent); + case "Addition": + return new IggyParseTree.AdditionExpression(rule, children, leftExtent, rightExtent); + case "Subtraction": + return new IggyParseTree.SubtractionExpression(rule, children, leftExtent, rightExtent); + case "GreaterEq": + return new IggyParseTree.GreaterEqExpression(rule, children, leftExtent, rightExtent); + case "LessEq": + return new IggyParseTree.LessEqExpression(rule, children, leftExtent, rightExtent); + case "Greater": + return new IggyParseTree.GreaterExpression(rule, children, leftExtent, rightExtent); + case "Less": + return new IggyParseTree.LessExpression(rule, children, leftExtent, rightExtent); + case "Equal": + return new IggyParseTree.EqualExpression(rule, children, leftExtent, rightExtent); + case "NotEqual": + return new IggyParseTree.NotEqualExpression(rule, children, leftExtent, rightExtent); + case "And": + return new IggyParseTree.AndExpression(rule, children, leftExtent, rightExtent); + case "Or": + return new IggyParseTree.OrExpression(rule, children, leftExtent, rightExtent); + case "LExtent": + return new IggyParseTree.LExtentExpression(rule, children, leftExtent, rightExtent); + case "RExtent": + return new IggyParseTree.RExtentExpression(rule, children, leftExtent, rightExtent); + case "Yield": + return new IggyParseTree.YieldExpression(rule, children, leftExtent, rightExtent); + case "Val": + return new IggyParseTree.ValExpression(rule, children, leftExtent, rightExtent); + case "Name": + return new IggyParseTree.NameExpression(rule, children, leftExtent, rightExtent); + case "Number": + return new IggyParseTree.NumberExpression(rule, children, leftExtent, rightExtent); + case "Bracket": + return new IggyParseTree.BracketExpression(rule, children, leftExtent, rightExtent); + default: + throw new RuntimeException("Unexpected label:" + label); + } + case "ReturnExpression": + return new IggyParseTree.ReturnExpression(rule, children, leftExtent, rightExtent); + case "VarName": + return new IggyParseTree.VarName(rule, children, leftExtent, rightExtent); + case "Name": + return new IggyParseTree.Name(rule, children, leftExtent, rightExtent); + case "FunName": + switch (label) { + case "Println": + return new IggyParseTree.PrintlnFunName(rule, children, leftExtent, rightExtent); + case "Indent": + return new IggyParseTree.IndentFunName(rule, children, leftExtent, rightExtent); + case "Assert": + return new IggyParseTree.AssertFunName(rule, children, leftExtent, rightExtent); + case "Set": + return new IggyParseTree.SetFunName(rule, children, leftExtent, rightExtent); + case "Contains": + return new IggyParseTree.ContainsFunName(rule, children, leftExtent, rightExtent); + case "Put": + return new IggyParseTree.PutFunName(rule, children, leftExtent, rightExtent); + default: + throw new RuntimeException("Unexpected label:" + label); + } + case "Identifier": + return new IggyParseTree.Identifier(rule, children, leftExtent, rightExtent); + case "Label": + return new IggyParseTree.Label(rule, children, leftExtent, rightExtent); + case "Layout": + return new IggyParseTree.Layout(rule, children, leftExtent, rightExtent); + case "$_Symbol": + return new IggyParseTree.$_Symbol(rule, children, leftExtent, rightExtent); + case "$_Expression": + return new IggyParseTree.$_Expression(rule, children, leftExtent, rightExtent); + default: + throw new RuntimeException("Unexpected nonterminal:" + name); + } + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/iggy/gen/IggyParseTreeVisitor.java b/benchmarks/src/main/kotlin/org/iguana/iggy/gen/IggyParseTreeVisitor.java new file mode 100644 index 000000000..69bd4fce1 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/iggy/gen/IggyParseTreeVisitor.java @@ -0,0 +1,209 @@ +// This file has been generated, do not directly edit this file! + +package org.iguana.iggy.gen; + +import org.iguana.parsetree.NonterminalNode; +import org.iguana.parsetree.ParseTreeVisitor; + +public interface IggyParseTreeVisitor extends ParseTreeVisitor { + + @Override + default T visitNonterminalNode(NonterminalNode node) { + throw new UnsupportedOperationException(); + } + + T visitGrammar(IggyParseTree.Grammar node); + + T visitTopLevelVar(IggyParseTree.TopLevelVar node); + + T visitContextFreeRule(IggyParseTree.ContextFreeRule node); + + T visitRegexRule(IggyParseTree.RegexRule node); + + T visitParameters(IggyParseTree.Parameters node); + + T visitRegexBody(IggyParseTree.RegexBody node); + + T visitBody(IggyParseTree.Body node); + + T visitPriorityLevels(IggyParseTree.PriorityLevels node); + + T visitSequenceAlternative(IggyParseTree.SequenceAlternative node); + + T visitAssociativityAlternative(IggyParseTree.AssociativityAlternative node); + + T visitMoreThanOneElemSequence(IggyParseTree.MoreThanOneElemSequence node); + + T visitSingleElemSequence(IggyParseTree.SingleElemSequence node); + + T visitEmptySequence(IggyParseTree.EmptySequence node); + + T visitCondition(IggyParseTree.Condition node); + + T visitCallSymbol(IggyParseTree.CallSymbol node); + + T visitOffsideSymbol(IggyParseTree.OffsideSymbol node); + + T visitStarSymbol(IggyParseTree.StarSymbol node); + + T visitPlusSymbol(IggyParseTree.PlusSymbol node); + + T visitOptionSymbol(IggyParseTree.OptionSymbol node); + + T visitSequenceSymbol(IggyParseTree.SequenceSymbol node); + + T visitAlternationSymbol(IggyParseTree.AlternationSymbol node); + + T visitAlignSymbol(IggyParseTree.AlignSymbol node); + + T visitIgnoreSymbol(IggyParseTree.IgnoreSymbol node); + + T visitLabeledSymbol(IggyParseTree.LabeledSymbol node); + + T visitStatementSymbol(IggyParseTree.StatementSymbol node); + + T visitPostConditionSymbol(IggyParseTree.PostConditionSymbol node); + + T visitPrecedeSymbol(IggyParseTree.PrecedeSymbol node); + + T visitNotPrecedeSymbol(IggyParseTree.NotPrecedeSymbol node); + + T visitStartOfLineSymbol(IggyParseTree.StartOfLineSymbol node); + + T visitFollowSymbol(IggyParseTree.FollowSymbol node); + + T visitNotFollowSymbol(IggyParseTree.NotFollowSymbol node); + + T visitExcludeSymbol(IggyParseTree.ExcludeSymbol node); + + T visitExceptSymbol(IggyParseTree.ExceptSymbol node); + + T visitEndOfLineSymbol(IggyParseTree.EndOfLineSymbol node); + + T visitEndOfFileSymbol(IggyParseTree.EndOfFileSymbol node); + + T visitIfThenElseSymbol(IggyParseTree.IfThenElseSymbol node); + + T visitIdentifierSymbol(IggyParseTree.IdentifierSymbol node); + + T visitStringSymbol(IggyParseTree.StringSymbol node); + + T visitCharClassSymbol(IggyParseTree.CharClassSymbol node); + + T visitStarSepSymbol(IggyParseTree.StarSepSymbol node); + + T visitPlusSepSymbol(IggyParseTree.PlusSepSymbol node); + + T visitErrorSymbol(IggyParseTree.ErrorSymbol node); + + T visitArguments(IggyParseTree.Arguments node); + + T visitCallStatement(IggyParseTree.CallStatement node); + + T visitBindingStatement(IggyParseTree.BindingStatement node); + + T visitAssignBinding(IggyParseTree.AssignBinding node); + + T visitDeclareBinding(IggyParseTree.DeclareBinding node); + + T visitStarRegex(IggyParseTree.StarRegex node); + + T visitPlusRegex(IggyParseTree.PlusRegex node); + + T visitOptionRegex(IggyParseTree.OptionRegex node); + + T visitBracketRegex(IggyParseTree.BracketRegex node); + + T visitSequenceRegex(IggyParseTree.SequenceRegex node); + + T visitAlternationRegex(IggyParseTree.AlternationRegex node); + + T visitNontRegex(IggyParseTree.NontRegex node); + + T visitCharClassRegex(IggyParseTree.CharClassRegex node); + + T visitStringRegex(IggyParseTree.StringRegex node); + + T visitCharsCharClass(IggyParseTree.CharsCharClass node); + + T visitNotCharsCharClass(IggyParseTree.NotCharsCharClass node); + + T visitRangeRange(IggyParseTree.RangeRange node); + + T visitCharacterRange(IggyParseTree.CharacterRange node); + + T visitCallExpression(IggyParseTree.CallExpression node); + + T visitNotExpression(IggyParseTree.NotExpression node); + + T visitMultiplicationExpression(IggyParseTree.MultiplicationExpression node); + + T visitDivisionExpression(IggyParseTree.DivisionExpression node); + + T visitAdditionExpression(IggyParseTree.AdditionExpression node); + + T visitSubtractionExpression(IggyParseTree.SubtractionExpression node); + + T visitGreaterEqExpression(IggyParseTree.GreaterEqExpression node); + + T visitLessEqExpression(IggyParseTree.LessEqExpression node); + + T visitGreaterExpression(IggyParseTree.GreaterExpression node); + + T visitLessExpression(IggyParseTree.LessExpression node); + + T visitEqualExpression(IggyParseTree.EqualExpression node); + + T visitNotEqualExpression(IggyParseTree.NotEqualExpression node); + + T visitAndExpression(IggyParseTree.AndExpression node); + + T visitOrExpression(IggyParseTree.OrExpression node); + + T visitLExtentExpression(IggyParseTree.LExtentExpression node); + + T visitRExtentExpression(IggyParseTree.RExtentExpression node); + + T visitYieldExpression(IggyParseTree.YieldExpression node); + + T visitValExpression(IggyParseTree.ValExpression node); + + T visitNameExpression(IggyParseTree.NameExpression node); + + T visitNumberExpression(IggyParseTree.NumberExpression node); + + T visitBracketExpression(IggyParseTree.BracketExpression node); + + T visitReturnExpression(IggyParseTree.ReturnExpression node); + + T visitVarName(IggyParseTree.VarName node); + + T visitName(IggyParseTree.Name node); + + T visitPrintlnFunName(IggyParseTree.PrintlnFunName node); + + T visitIndentFunName(IggyParseTree.IndentFunName node); + + T visitAssertFunName(IggyParseTree.AssertFunName node); + + T visitSetFunName(IggyParseTree.SetFunName node); + + T visitContainsFunName(IggyParseTree.ContainsFunName node); + + T visitPutFunName(IggyParseTree.PutFunName node); + + T visitIdentifier(IggyParseTree.Identifier node); + + T visitLabel(IggyParseTree.Label node); + + T visitLayout(IggyParseTree.Layout node); + + default T visit$_Symbol(IggyParseTree.$_Symbol node) { + return node.child().accept(this); + } + + default T visit$_Expression(IggyParseTree.$_Expression node) { + return node.child().accept(this); + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/iggy/gen/IggyParser.java b/benchmarks/src/main/kotlin/org/iguana/iggy/gen/IggyParser.java new file mode 100644 index 000000000..44b8caa6e --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/iggy/gen/IggyParser.java @@ -0,0 +1,30 @@ +// This file has been generated, do not directly edit this file! + +package org.iguana.iggy.gen; + +import org.iguana.grammar.Grammar; +import org.iguana.parser.IguanaParser; +import org.iguana.parsetree.ParseTreeBuilder; +import org.iguana.parsetree.ParseTreeNode; +import org.iguana.utils.input.Input; + +public class IggyParser extends IguanaParser { + + private static IggyParser parser; + + private IggyParser(Grammar grammar) { + super(grammar); + } + + public static IggyParser getInstance() { + if (parser == null) { + parser = new IggyParser(IggyGrammar.getGrammar()); + } + return parser; + } + + @Override + protected ParseTreeBuilder getParseTreeBuilder(Input input) { + return new IggyParseTreeBuilder(input); + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/parser/IguanaParser.java b/benchmarks/src/main/kotlin/org/iguana/parser/IguanaParser.java new file mode 100644 index 000000000..2f6e4ad9a --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/parser/IguanaParser.java @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.parser; + +import org.iguana.grammar.Grammar; +import org.iguana.grammar.runtime.RuntimeGrammar; +import org.iguana.grammar.slot.ErrorTransition; +import org.iguana.grammar.symbol.Nonterminal; +import org.iguana.grammar.symbol.Start; +import org.iguana.grammar.symbol.Symbol; +import org.iguana.gss.GSSEdge; +import org.iguana.gss.GSSNode; +import org.iguana.parser.options.ParseOptions; +import org.iguana.parser.options.ParseTreeOptions; +import org.iguana.parsetree.DefaultParseTreeBuilder; +import org.iguana.parsetree.ParseTreeBuilder; +import org.iguana.parsetree.ParseTreeNode; +import org.iguana.result.ParserResultOps; +import org.iguana.sppf.NonPackedNode; +import org.iguana.sppf.NonterminalNode; +import org.iguana.traversal.AmbiguousSPPFToParseTreeVisitor; +import org.iguana.traversal.DefaultSPPFToParseTreeVisitor; +import org.iguana.util.Configuration; +import org.iguana.util.Tuple; +import org.iguana.utils.input.Input; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.PriorityQueue; + +import static org.iguana.parser.options.ParseOptions.defaultOptions; + +public class IguanaParser extends IguanaRecognizer { + + private static final ParserResultOps parserResultOps = new ParserResultOps(); + + private ParseTreeNode parseTree; + private NonterminalNode sppf; + private Input input; + + public IguanaParser(Grammar grammar) { + this(grammar, Configuration.load()); + } + + public IguanaParser(Grammar grammar, Configuration config) { + super(grammar, config); + } + + public IguanaParser(RuntimeGrammar grammar) { + this(grammar, Configuration.load()); + } + + public IguanaParser(RuntimeGrammar grammar, Configuration config) { + super(grammar, config); + } + + public void parse(Input input, Symbol symbol, ParseOptions parseOptions) { + if (symbol instanceof Nonterminal) parse(input, (Nonterminal) symbol, parseOptions); + else if (symbol instanceof Start) parse(input, (Start) symbol, parseOptions); + else throw new RuntimeException("Symbol should be a nonterminal or start, but was: " + symbol.getClass()); + } + + public void parse(Input input, Symbol symbol) { + parse(input, symbol, ParseOptions.defaultOptions()); + } + + public void parse(Input input, Start start) { + parse(input, Nonterminal.withName(assertStartSymbolNotNull(start).getName()), defaultOptions()); + } + + public void parse(Input input, Start start, ParseOptions parseOptions) { + parse(input, Nonterminal.withName(assertStartSymbolNotNull(start).getName()), parseOptions); + } + + public void parse(Input input, Nonterminal nonterminal) { + parse(input, nonterminal, defaultOptions()); + } + + public void parse(Input input, Nonterminal start, ParseOptions parseOptions) { + clear(); + this.input = input; + IguanaRuntime runtime = new IguanaRuntime<>(config, parserResultOps); + long startTime = System.nanoTime(); + this.sppf = (NonterminalNode) runtime.run(input, start, grammarGraph, parseOptions.getMap(), + parseOptions.isGlobal()); + long endTime = System.nanoTime(); + this.statistics = runtime.getStatistics(); + if (sppf == null) { + if (parseOptions.isErrorRecoveryEnabled()) { + PriorityQueue> parseErrors = runtime.getParseErrors(); + outer: + while (!parseErrors.isEmpty()) { + List, ErrorTransition>> errorSlots = new ArrayList<>(); + GSSNode gssNode = parseErrors.poll().getGssNode(); + runtime.collectErrorSlots(gssNode, errorSlots, new HashSet<>()); + for (Tuple, ErrorTransition> t : errorSlots) { + runtime.recoverFromError(t.getFirst(), t.getSecond(), input); + NonPackedNode recoveryResult = runtime.runParserLoop(runtime.getStartGSSNode(), input); + if (recoveryResult != null) { + sppf = (NonterminalNode) recoveryResult; + break outer; + } + } + } + } + if (sppf == null) { + this.parseError = runtime.getParseErrors().peek(); + throw new ParseErrorException(parseError); + } + } else { + System.out.println("Parsing finished in " + (endTime - startTime) / 1000_000 + "ms."); + } + } + + @Override + protected void clear() { + super.clear(); + this.sppf = null; + this.parseTree = null; + this.input = null; + } + + public NonterminalNode getSPPF() { + return sppf; + } + + public ParseTreeNode getParseTree() { + return getParseTree(ParseTreeOptions.defaultOptions()); + } + + public ParseTreeNode getParseTree(ParseTreeOptions options) { + if (parseTree != null) return parseTree; + + if (sppf == null) return null; + + boolean allowAmbiguities = options.allowAmbiguities(); + boolean ignoreLayout = options.ignoreLayout(); + + if (allowAmbiguities) { + AmbiguousSPPFToParseTreeVisitor visitor = + new AmbiguousSPPFToParseTreeVisitor<>(getParseTreeBuilder(input), ignoreLayout, parserResultOps); + long start = System.nanoTime(); + ParseTreeNode node = (ParseTreeNode) sppf.accept(visitor).getValues().get(0); + long end = System.nanoTime(); + System.out.println("Parse tree creation finished in " + (end - start) / 1000_000 + "ms."); + return node; + } + + DefaultSPPFToParseTreeVisitor visitor = + new DefaultSPPFToParseTreeVisitor<>(getParseTreeBuilder(input), input, ignoreLayout, parserResultOps); + long start = System.nanoTime(); + this.parseTree = sppf.accept(visitor); + long end = System.nanoTime(); + System.out.println("Parse tree creation finished in " + (end - start) / 1000_000 + "ms."); + + return parseTree; + } + + public ParseStatistics getStatistics() { + return (ParseStatistics) statistics; + } + + protected ParseTreeBuilder getParseTreeBuilder(Input input) { + return new DefaultParseTreeBuilder(input); + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/parser/IguanaRecognizer.java b/benchmarks/src/main/kotlin/org/iguana/parser/IguanaRecognizer.java new file mode 100644 index 000000000..e789dc60e --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/parser/IguanaRecognizer.java @@ -0,0 +1,98 @@ +package org.iguana.parser; + +import org.iguana.grammar.Grammar; +import org.iguana.grammar.GrammarGraph; +import org.iguana.grammar.GrammarGraphBuilder; +import org.iguana.grammar.runtime.RuntimeGrammar; +import org.iguana.grammar.symbol.Nonterminal; +import org.iguana.grammar.symbol.Start; +import org.iguana.grammar.symbol.Symbol; +import org.iguana.grammar.transformation.GrammarTransformer; +import org.iguana.parser.options.RecognizerOptions; +import org.iguana.result.RecognizerResult; +import org.iguana.result.RecognizerResultOps; +import org.iguana.util.Configuration; +import org.iguana.utils.input.Input; + +public class IguanaRecognizer { + + private static final RecognizerResultOps recognizerResultOps = new RecognizerResultOps(); + + protected final GrammarGraph grammarGraph; + protected final Configuration config; + + protected ParseError parseError; + protected RecognizerStatistics statistics; + + protected final RuntimeGrammar finalGrammar; + + public IguanaRecognizer(Grammar grammar) { + this(grammar, Configuration.load()); + } + + public IguanaRecognizer(Grammar grammar, Configuration config) { + this(GrammarTransformer.transform(grammar.toRuntimeGrammar()), config); + } + + public IguanaRecognizer(RuntimeGrammar grammar) { + this(grammar, Configuration.load()); + } + + public IguanaRecognizer(RuntimeGrammar grammar, Configuration config) { + this.grammarGraph = GrammarGraphBuilder.from(grammar, config); + this.config = config; + this.finalGrammar = grammar; + } + + public boolean recognize(Input input, Symbol symbol) { + if (symbol instanceof Nonterminal) return recognize(input, (Nonterminal) symbol); + else if (symbol instanceof Start) return recognize(input, (Start) symbol); + else throw new RuntimeException("Symbol should be a nonterminal or start, but was: " + symbol.getClass()); + } + + public boolean recognize(Input input, Nonterminal nonterminal) { + return recognize(input, nonterminal, RecognizerOptions.defaultOptions()); + } + + public boolean recognize(Input input, Start start) { + return recognize(input, Nonterminal.withName(assertStartSymbolNotNull(start).getName()), + RecognizerOptions.defaultOptions()); + } + + public boolean recognize(Input input, Nonterminal start, RecognizerOptions options) { + clear(); + IguanaRuntime runtime = new IguanaRuntime<>(config, recognizerResultOps); + RecognizerResult result = runtime.run(input, start, grammarGraph, options.getMap(), options.isGlobal()); + this.statistics = runtime.getStatistics(); + if (result == null) { + this.parseError = runtime.getParseErrors().peek(); + return false; + } + return true; + } + + public RecognizerStatistics getStatistics() { + return statistics; + } + + public ParseError getParseError() { + return parseError; + } + + public RuntimeGrammar getFinalGrammar() { + return finalGrammar; + } + + protected void clear() { + grammarGraph.clear(); + parseError = null; + statistics = null; + } + + protected static Start assertStartSymbolNotNull(Start start) { + if (start == null) { + throw new RuntimeException("Start symbol is not set"); + } + return start; + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/parser/IguanaRuntime.java b/benchmarks/src/main/kotlin/org/iguana/parser/IguanaRuntime.java new file mode 100644 index 000000000..d1f930222 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/parser/IguanaRuntime.java @@ -0,0 +1,395 @@ +package org.iguana.parser; + +import org.iguana.datadependent.ast.Expression; +import org.iguana.datadependent.ast.Statement; +import org.iguana.datadependent.env.Environment; +import org.iguana.datadependent.env.GLLEvaluator; +import org.iguana.datadependent.env.IEvaluatorContext; +import org.iguana.grammar.GrammarGraph; +import org.iguana.grammar.slot.BodyGrammarSlot; +import org.iguana.grammar.slot.ErrorTransition; +import org.iguana.grammar.slot.GrammarSlot; +import org.iguana.grammar.slot.NonterminalGrammarSlot; +import org.iguana.grammar.slot.TerminalGrammarSlot; +import org.iguana.grammar.symbol.Nonterminal; +import org.iguana.gss.CyclicDummyGSSEdges; +import org.iguana.gss.CyclicDummyGSSEdgesWithEnv; +import org.iguana.gss.DefaultGSSEdge; +import org.iguana.gss.DefaultGSSEdgeWithEnv; +import org.iguana.gss.DummyGSSEdge; +import org.iguana.gss.DummyGSSEdgeWithEnv; +import org.iguana.gss.GSSEdge; +import org.iguana.gss.GSSNode; +import org.iguana.gss.StartGSSNode; +import org.iguana.parser.descriptor.Descriptor; +import org.iguana.result.ParserResultOps; +import org.iguana.result.Result; +import org.iguana.result.ResultOps; +import org.iguana.util.Configuration; +import org.iguana.util.ParserLogger; +import org.iguana.util.Tuple; +import org.iguana.utils.input.Input; + +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.Deque; +import java.util.List; +import java.util.Map; +import java.util.PriorityQueue; +import java.util.Set; +import java.util.function.Function; + +public class IguanaRuntime { + + private final Deque> descriptorPool; + + private final Deque> descriptorsStack; + + private final IEvaluatorContext ctx; + + private final Configuration config; + + private final ParserLogger logger = ParserLogger.getInstance(); + + private final ResultOps resultOps; + + // A priority queue (max heap) containing the parse errors thrown the parsing, sorted by the input index. + // The top of the priority queue is the parse error thrown at the last input position. + private final PriorityQueue> parseErrors; + + private StartGSSNode startGSSNode; + + public IguanaRuntime(Configuration config, ResultOps resultOps) { + this.config = config; + this.resultOps = resultOps; + this.descriptorsStack = new ArrayDeque<>(512); + this.descriptorPool = new ArrayDeque<>(512); + this.ctx = GLLEvaluator.getEvaluatorContext(config); + this.parseErrors = new PriorityQueue<>((error1, error2) -> error2.getInputIndex() - error1.getInputIndex()); + } + + public T run(Input input, Nonterminal start, GrammarGraph grammarGraph, Map map, boolean global) { + clearState(grammarGraph); + + IEvaluatorContext ctx = getEvaluatorContext(); + + if (global) + map.forEach(ctx::declareGlobalVariable); + + Environment env = ctx.getEmptyEnvironment(); + + for (Map.Entry entry : map.entrySet()) { + env = env._declare(entry.getKey(), entry.getValue()); + } + + for (Map.Entry entry : grammarGraph.getGlobals().entrySet()) { + env = env._declare(entry.getKey(), entry.getValue().interpret(ctx, input)); + } + + NonterminalGrammarSlot startSlot = grammarGraph.getStartSlot(start); + List parameters = startSlot.getParameters(); + // TODO: Make parameters an empty list by default + if (parameters != null && !parameters.isEmpty()) { + if (!global && !env.isEmpty()) { + Object[] arguments = new Object[parameters.size()]; + + int i = 0; + for (String parameter : startSlot.getParameters()) { + arguments[i++] = env.lookup(parameter); + } + + startGSSNode = new StartGSSNode<>(startSlot, 0, arguments); + env = env.declare(startSlot.getParameters().toArray(new String[0]), arguments); + } else { + startSlot = grammarGraph.getStartSlot(Nonterminal.withName("$_" + start.getName())); + if (startSlot == null) { + throw new RuntimeException( + "No top level definition exists for " + start.getName() + " " + parameters); + } + startGSSNode = new StartGSSNode<>(startSlot, 0); + } + } else { + startGSSNode = new StartGSSNode<>(startSlot, 0); + } + + ParserLogger logger = ParserLogger.getInstance(); + logger.reset(); + + for (BodyGrammarSlot slot : startSlot.getFirstSlots()) { + scheduleDescriptor(slot, startGSSNode, getResultOps().dummy(), env); + } + + return runParserLoop(startGSSNode, input); + } + + public T runParserLoop(StartGSSNode startGSSNode, Input input) { + while (hasDescriptor()) { + Descriptor descriptor = nextDescriptor(); + logger.processDescriptor(descriptor); + descriptor.getGrammarSlot().execute(input, descriptor.getGSSNode(), descriptor.getResult(), + descriptor.getEnv(), this); + } + + int inputLength = input.length() - 1; + T result = startGSSNode.getResult(inputLength); + // If there was a successful parse (covering the whole input range) for the start symbol + if (result != null && result.getRightExtent() == inputLength) { + return result; + } + return null; + } + + private void clearState(GrammarGraph grammarGraph) { + grammarGraph.clear(); + descriptorPool.clear(); + descriptorsStack.clear(); + parseErrors.clear(); + } + + public void recordParseError( + int inputIndex, + Input input, + GrammarSlot slot, + GSSNode gssNode, + String description) { + ParseError error = new ParseError<>(slot, gssNode, inputIndex, input.getLineNumber(inputIndex), + input.getColumnNumber(inputIndex), description); + parseErrors.add(error); + logger.error(error); + } + + /* + * Tries to recover the error from an error edge, i.e., of the form X = alpha . Error beta + */ + public void recoverFromError(GSSEdge edge, ErrorTransition errorTransition, Input input) { + T result = edge instanceof DummyGSSEdge ? resultOps.dummy() : edge.getResult(); + Environment env = edge.getEnv(); + errorTransition.handleError(input, edge.getDestination(), result, env, this); + } + + // Collects all the GSS edges with the label of the form X = alpha . Error beta that are reachable + // from the current GSS node to the start symbol GSS node. + public void collectErrorSlots( + GSSNode gssNode, + List, ErrorTransition>> result, + Set> visited) { + if (visited.contains(gssNode)) return; + visited.add(gssNode); + if (gssNode == null) return; + for (GSSEdge edge : gssNode.getGSSEdges()) { + ErrorTransition errorTransition = getErrorTransition(edge); + if (errorTransition != null) { + result.add(Tuple.of(edge, errorTransition)); + } + collectErrorSlots(edge.getDestination(), result, visited); + } + } + + private ErrorTransition getErrorTransition(GSSEdge edge) { + if (edge.getReturnSlot() == null) return null; + // This covers the cases with no layout insertion: X alpha . Error + if (edge.getReturnSlot().getOutTransition() instanceof ErrorTransition) { + return (ErrorTransition) edge.getReturnSlot().getOutTransition(); + } + // This covers cases where the layout is inserted. + // X = alpha . Layout Error + if (edge.getReturnSlot().getOutTransition() != null + && edge.getReturnSlot().getOutTransition().destination() != null + && edge.getReturnSlot().getOutTransition().destination().getOutTransition() instanceof ErrorTransition) { + return (ErrorTransition) edge.getReturnSlot().getOutTransition().destination().getOutTransition(); + } + return null; + } + + public boolean hasDescriptor() { + return !descriptorsStack.isEmpty(); + } + + public Descriptor nextDescriptor() { + Descriptor descriptor = descriptorsStack.pop(); + descriptorPool.push(descriptor); + return descriptor; + } + + public void scheduleDescriptor(BodyGrammarSlot grammarSlot, GSSNode gssNode, T result, Environment env) { + Descriptor descriptor; + if (!descriptorPool.isEmpty()) { + descriptor = descriptorPool.pop(); + descriptor.init(grammarSlot, gssNode, result, env); + } else { + descriptor = new Descriptor<>(grammarSlot, gssNode, result, env); + } + descriptorsStack.push(descriptor); + logger.descriptorAdded(descriptor); + } + + public IEvaluatorContext getEvaluatorContext() { + return ctx; + } + + public Environment getEnvironment() { + return ctx.getEnvironment(); + } + + public void setEnvironment(Environment env) { + ctx.setEnvironment(env); + } + + public Environment getEmptyEnvironment() { + return ctx.getEmptyEnvironment(); + } + + public GSSEdge createGSSEdge(BodyGrammarSlot returnSlot, T result, GSSNode gssNode, Environment env) { + if (result.isDummy()) { + if (env.isEmpty()) { + return gssNode != null ? new DummyGSSEdge<>(returnSlot, gssNode) : new CyclicDummyGSSEdges<>(); + } else { + return gssNode != null ? new DummyGSSEdgeWithEnv<>(returnSlot, gssNode, env) + : new CyclicDummyGSSEdgesWithEnv<>(env); + } + } + + if (env.isEmpty()) { + return new DefaultGSSEdge<>(returnSlot, result, gssNode); + } else { + return new DefaultGSSEdgeWithEnv<>(returnSlot, result, gssNode, env); + } + } + + public void evaluate(Statement[] statements, Environment env, Input input) { + assert statements.length > 0; + + ctx.setEnvironment(env); + + int i = 0; + while (i < statements.length) { + statements[i].interpret(ctx, input); + i++; + } + } + + public Object evaluate(Expression expression, Environment env, Input input) { + ctx.setEnvironment(env); + return expression.interpret(ctx, input); + } + + public Object[] evaluate(Expression[] arguments, Environment env, Input input) { + if (arguments == null) return null; + + ctx.setEnvironment(env); + + Object[] values = new Object[arguments.length]; + + int i = 0; + while (i < arguments.length) { + values[i] = arguments[i].interpret(ctx, input); + i++; + } + + return values; + } + + public PriorityQueue> getParseErrors() { + return new PriorityQueue<>(parseErrors); + } + + public RecognizerStatistics getStatistics() { + if (resultOps instanceof ParserResultOps) { + return ParseStatistics.builder() + .setDescriptorsCount(logger.getDescriptorsCount()) + .setGSSNodesCount(logger.getCountGSSNodes() + 1) // + start gss node + .setGSSEdgesCount(logger.getCountGSSEdges()) + .setNonterminalNodesCount(logger.getCountNonterminalNodes()) + .setTerminalNodesCount(logger.getCountTerminalNodes()) + .setIntermediateNodesCount(logger.getCountIntermediateNodes()) + .setPackedNodesCount(logger.getCountPackedNodes()) + .setAmbiguousNodesCount(logger.getCountAmbiguousNodes()) + .build(); + } else { + return RecognizerStatistics.builder() + .setDescriptorsCount(logger.getDescriptorsCount()) + .setGSSNodesCount(logger.getCountGSSNodes() + 1) // + start gss node + .setGSSEdgesCount(logger.getCountGSSEdges()) + .build(); + } + + } + + public Configuration getConfiguration() { + return config; + } + + public int getDescriptorPoolSize() { + return descriptorPool.size(); + } + + public ResultOps getResultOps() { + return resultOps; + } + + public StartGSSNode getStartGSSNode() { + return startGSSNode; + } + + private static void printStats(GrammarGraph grammarGraph) { + for (TerminalGrammarSlot slot : grammarGraph.getTerminalGrammarSlots()) { + System.out.println(slot.getTerminal().getName() + " : " + slot.countTerminalNodes()); + } + + for (NonterminalGrammarSlot slot : grammarGraph.getNonterminalGrammarSlots()) { + System.out.print(slot.getNonterminal().getName()); + System.out.println(" GSS nodes: " + slot.countGSSNodes()); + double[] poppedElementStats = stats(slot.getGSSNodes(), GSSNode::countPoppedElements); + double[] gssEdgesStats = stats(slot.getGSSNodes(), GSSNode::countGSSEdges); + if (poppedElementStats == null) + System.out.println("Popped Elements: empty"); + else + System.out.printf("Popped Elements (min: %d, max: %d, mean: %.2f)%n", (int) poppedElementStats[0], + (int) poppedElementStats[1], poppedElementStats[2]); + + if (gssEdgesStats == null) + System.out.println("GSS Edges: empty"); + else + System.out.printf("GSS Edges (min: %d, max: %d, mean: %.2f)%n", (int) gssEdgesStats[0], + (int) gssEdgesStats[1], gssEdgesStats[2]); + System.out.println("---------------"); + } + } + + private static void printGSSInfo(GrammarGraph grammarGraph) { + Comparator> edgeComparator = (node1, node2) -> node2.countGSSEdges() - node1.countGSSEdges(); + List> gssNodes = new ArrayList<>(); + for (NonterminalGrammarSlot slot : grammarGraph.getNonterminalGrammarSlots()) { + for (GSSNode gssNode : slot.getGSSNodes()) { + gssNodes.add(gssNode); + } + } + + gssNodes.sort(edgeComparator); + + for (GSSNode gssNode : gssNodes) { + System.out.println( + gssNode + ", edges: " + gssNode.countGSSEdges() + ", poppedElements: " + + gssNode.countPoppedElements()); + } + } + + private static double[] stats(Iterable> gssNodes, Function, Integer> f) { + if (!gssNodes.iterator().hasNext()) return null; + + int min = Integer.MAX_VALUE; + int max = Integer.MIN_VALUE; + int sum = 0; + int count = 0; + + for (GSSNode gssNode : gssNodes) { + min = Integer.min(min, f.apply(gssNode)); + max = Integer.max(max, f.apply(gssNode)); + sum += f.apply(gssNode); + count++; + } + + return new double[]{min, max, (double) sum / count}; + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/parser/ParseError.java b/benchmarks/src/main/kotlin/org/iguana/parser/ParseError.java new file mode 100644 index 000000000..74fe57bfb --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/parser/ParseError.java @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.parser; + +import org.iguana.grammar.slot.GrammarSlot; +import org.iguana.gss.GSSNode; +import org.iguana.result.Result; + +import java.util.Objects; + + +public class ParseError { + + private final GrammarSlot slot; + private final int inputIndex; + private final int lineNumber; + private final int columnNumber; + private final GSSNode gssNode; + private final String description; + + public ParseError( + GrammarSlot slot, + GSSNode gssNode, + int inputIndex, + int lineNumber, + int columnNumber, + String description) { + this.slot = slot; + this.inputIndex = inputIndex; + this.lineNumber = lineNumber; + this.columnNumber = columnNumber; + this.description = description; + this.gssNode = gssNode; + } + + public int getInputIndex() { + return inputIndex; + } + + public GrammarSlot getGrammarSlot() { + return slot; + } + + public int getLineNumber() { + return lineNumber; + } + + public int getColumnNumber() { + return columnNumber; + } + + public String getDescription() { + return description; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (!(obj instanceof ParseError)) return false; + ParseError other = (ParseError) obj; + return inputIndex == other.inputIndex + && lineNumber == other.lineNumber + && columnNumber == other.columnNumber; + } + + public GSSNode getGssNode() { + return gssNode; + } + + @Override + public int hashCode() { + return Objects.hash(inputIndex, lineNumber, columnNumber); + } + + @Override + public String toString() { + return String.format("Parse error at input index: %d, line: %d, column: %d, grammar rule: %s, description: %s", + inputIndex, lineNumber, columnNumber, slot, description); + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/parser/ParseErrorException.java b/benchmarks/src/main/kotlin/org/iguana/parser/ParseErrorException.java new file mode 100644 index 000000000..1618303ee --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/parser/ParseErrorException.java @@ -0,0 +1,20 @@ +package org.iguana.parser; + +@SuppressWarnings("serial") +public class ParseErrorException extends RuntimeException { + + private final ParseError parseError; + + public ParseErrorException(ParseError parseError) { + this.parseError = parseError; + } + + public ParseError getParseError() { + return parseError; + } + + @Override + public String toString() { + return parseError.toString(); + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/parser/ParseStatistics.java b/benchmarks/src/main/kotlin/org/iguana/parser/ParseStatistics.java new file mode 100644 index 000000000..e328545ab --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/parser/ParseStatistics.java @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.parser; + +import java.util.Objects; + +public class ParseStatistics extends RecognizerStatistics { + private final int nonterminalNodesCount; + private final int terminalNodesCount; + private final int intermediateNodesCount; + private final int packedNodesCount; + private final int ambiguousNodesCount; + + public ParseStatistics(Builder builder) { + super(builder); + this.nonterminalNodesCount = builder.nonterminalNodesCount; + this.terminalNodesCount = builder.terminalNodesCount; + this.intermediateNodesCount = builder.intermediateNodesCount; + this.packedNodesCount = builder.packedNodesCount; + this.ambiguousNodesCount = builder.ambiguousNodesCount; + } + + public int getNonterminalNodesCount() { + return nonterminalNodesCount; + } + + public int getTerminalNodesCount() { + return terminalNodesCount; + } + + public int getIntermediateNodesCount() { + return intermediateNodesCount; + } + + public int getPackedNodesCount() { + return packedNodesCount; + } + + public int getAmbiguousNodesCount() { + return ambiguousNodesCount; + } + + public static Builder builder() { + return new Builder(); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), nonterminalNodesCount, + terminalNodesCount, intermediateNodesCount, packedNodesCount, + ambiguousNodesCount); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + + if (!(obj instanceof ParseStatistics)) return false; + + ParseStatistics other = (ParseStatistics) obj; + + return super.equals(obj) + && nonterminalNodesCount == other.nonterminalNodesCount + && terminalNodesCount == other.terminalNodesCount + && intermediateNodesCount == other.intermediateNodesCount + && packedNodesCount == other.packedNodesCount + && ambiguousNodesCount == other.ambiguousNodesCount; + } + + @Override + public String toString() { + return super.toString() + + "Nonterminal nodes: " + nonterminalNodesCount + "\n" + + "Terminal nodes: " + terminalNodesCount + "\n" + + "Intermediate nodes: " + intermediateNodesCount + "\n" + + "Packed nodes: " + packedNodesCount + "\n" + + "Ambiguities: " + ambiguousNodesCount + "\n"; + } + + public static class Builder extends RecognizerStatistics.Builder { + int nonterminalNodesCount; + int terminalNodesCount; + int intermediateNodesCount; + int packedNodesCount; + int ambiguousNodesCount; + + public Builder setDescriptorsCount(int descriptorsCount) { + this.descriptorsCount = descriptorsCount; + return this; + } + + public Builder setGSSNodesCount(int gssNodesCount) { + this.gssNodesCount = gssNodesCount; + return this; + } + + public Builder setGSSEdgesCount(int gssEdgesCount) { + this.gssEdgesCount = gssEdgesCount; + return this; + } + + public Builder setNonterminalNodesCount(int nonterminalNodesCount) { + this.nonterminalNodesCount = nonterminalNodesCount; + return this; + } + + public Builder setTerminalNodesCount(int terminalNodesCount) { + this.terminalNodesCount = terminalNodesCount; + return this; + } + + public Builder setIntermediateNodesCount(int intermediateNodesCount) { + this.intermediateNodesCount = intermediateNodesCount; + return this; + } + + public Builder setPackedNodesCount(int packedNodesCount) { + this.packedNodesCount = packedNodesCount; + return this; + } + + public Builder setAmbiguousNodesCount(int ambiguousNodesCount) { + this.ambiguousNodesCount = ambiguousNodesCount; + return this; + } + + public ParseStatistics build() { + return new ParseStatistics(this); + } + + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/parser/RecognizerStatistics.java b/benchmarks/src/main/kotlin/org/iguana/parser/RecognizerStatistics.java new file mode 100644 index 000000000..f90694d09 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/parser/RecognizerStatistics.java @@ -0,0 +1,81 @@ +package org.iguana.parser; + +import java.util.Objects; + +public class RecognizerStatistics { + + private final int descriptorsCount; + private final int gssNodesCount; + private final int gssEdgesCount; + + public RecognizerStatistics(Builder builder) { + this.descriptorsCount = builder.descriptorsCount; + this.gssNodesCount = builder.gssNodesCount; + this.gssEdgesCount = builder.gssEdgesCount; + } + + public int getDescriptorsCount() { + return descriptorsCount; + } + + public int getGssNodesCount() { + return gssNodesCount; + } + + public int getGssEdgesCount() { + return gssEdgesCount; + } + + @Override + public int hashCode() { + return Objects.hash(descriptorsCount, gssEdgesCount, gssEdgesCount); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (!(obj instanceof RecognizerStatistics)) return false; + RecognizerStatistics other = (RecognizerStatistics) obj; + + return descriptorsCount == other.descriptorsCount + && gssNodesCount == other.gssNodesCount + && gssEdgesCount == other.gssEdgesCount; + } + + @Override + public String toString() { + return "Descriptors: " + descriptorsCount + "\n" + + "GSS Nodes: " + gssNodesCount + "\n" + + "GSS Edges: " + gssEdgesCount + "\n"; + } + + public static Builder builder() { + return new Builder<>(); + } + + public static class Builder { + int descriptorsCount; + int gssNodesCount; + int gssEdgesCount; + + public Builder setDescriptorsCount(int descriptorsCount) { + this.descriptorsCount = descriptorsCount; + return this; + } + + public Builder setGSSNodesCount(int gssNodesCount) { + this.gssNodesCount = gssNodesCount; + return this; + } + + public Builder setGSSEdgesCount(int gssEdgesCount) { + this.gssEdgesCount = gssEdgesCount; + return this; + } + + public RecognizerStatistics build() { + return new RecognizerStatistics(this); + } + + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/parser/descriptor/Descriptor.java b/benchmarks/src/main/kotlin/org/iguana/parser/descriptor/Descriptor.java new file mode 100644 index 000000000..3d5753a30 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/parser/descriptor/Descriptor.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.parser.descriptor; + +import org.iguana.datadependent.env.Environment; +import org.iguana.grammar.slot.BodyGrammarSlot; +import org.iguana.gss.GSSNode; +import org.iguana.result.Result; + +public class Descriptor { + + // L + private BodyGrammarSlot slot; + + // (L1, i) + private GSSNode gssNode; + + // (L, i, j), The label of SPPFNode is the same as the slot + private T result; + + private Environment env; + + public Descriptor(BodyGrammarSlot slot, GSSNode gssNode, T result, Environment env) { + init(slot, gssNode, result, env); + } + + public void init(BodyGrammarSlot slot, GSSNode gssNode, T result, Environment env) { + this.slot = slot; + this.gssNode = gssNode; + this.result = result; + this.env = env; + } + + public BodyGrammarSlot getGrammarSlot() { + return slot; + } + + public GSSNode getGSSNode() { + return gssNode; + } + + public T getResult() { + return result; + } + + public Environment getEnv() { + return env; + } + + @Override + public String toString() { + return String.format("(%s, %s, %s, %s)", slot, gssNode, result, env); + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/parser/options/ParseOptions.java b/benchmarks/src/main/kotlin/org/iguana/parser/options/ParseOptions.java new file mode 100644 index 000000000..affd2734d --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/parser/options/ParseOptions.java @@ -0,0 +1,32 @@ +package org.iguana.parser.options; + +public class ParseOptions extends RecognizerOptions { + + private final boolean errorRecoveryEnabled; + + private ParseOptions(Builder builder) { + super(builder); + this.errorRecoveryEnabled = builder.errorRecoveryEnabled; + } + + public boolean isErrorRecoveryEnabled() { + return errorRecoveryEnabled; + } + + public static ParseOptions defaultOptions() { + return new Builder().build(); + } + + public static class Builder extends RecognizerOptions.Builder { + private boolean errorRecoveryEnabled = false; + + public Builder setErrorRecoveryEnabled(boolean errorRecoveryEnabled) { + this.errorRecoveryEnabled = errorRecoveryEnabled; + return this; + } + + public ParseOptions build() { + return new ParseOptions(this); + } + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/parser/options/ParseTreeOptions.java b/benchmarks/src/main/kotlin/org/iguana/parser/options/ParseTreeOptions.java new file mode 100644 index 000000000..680c92c5e --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/parser/options/ParseTreeOptions.java @@ -0,0 +1,51 @@ +package org.iguana.parser.options; + +public class ParseTreeOptions { + /** + * Ignore the layout nodes when converting the SPPF into the parse tree. + */ + private final boolean ignoreLayout; + + /** + * If set to false (default value) allow converting ambiguous SPPF to parse trees. Otherwise, throws + * an exception while traversing the SPPF. + */ + private final boolean allowAmbiguities; + + private ParseTreeOptions(Builder builder) { + this.ignoreLayout = builder.ignoreLayout; + this.allowAmbiguities = builder.allowAmbiguities; + } + + public static ParseTreeOptions defaultOptions() { + return new Builder().build(); + } + + public boolean ignoreLayout() { + return ignoreLayout; + } + + public boolean allowAmbiguities() { + return allowAmbiguities; + } + + public static class Builder { + private boolean allowAmbiguities = false; + private boolean ignoreLayout = true; + + public Builder setAllowAmbiguities(boolean allowAmbiguities) { + this.allowAmbiguities = allowAmbiguities; + return this; + } + + public Builder setIgnoreLayout(boolean ignoreLayout) { + this.ignoreLayout = ignoreLayout; + return this; + } + + public ParseTreeOptions build() { + return new ParseTreeOptions(this); + } + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/parser/options/RecognizerOptions.java b/benchmarks/src/main/kotlin/org/iguana/parser/options/RecognizerOptions.java new file mode 100644 index 000000000..22adcd86f --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/parser/options/RecognizerOptions.java @@ -0,0 +1,53 @@ +package org.iguana.parser.options; + +import java.util.Map; + +import static java.util.Collections.emptyMap; + +public class RecognizerOptions { + /** + * The user-provided key-value map to set the value of the top-level variables in the environment + */ + private final Map map; + + /** + * If variables are global. TODO: figure it out if this is still useful. + */ + private final boolean global; + + protected RecognizerOptions(Builder builder) { + this.map = builder.map; + this.global = builder.global; + } + + public static RecognizerOptions defaultOptions() { + return new Builder().build(); + } + + public Map getMap() { + return map; + } + + public boolean isGlobal() { + return global; + } + + public static class Builder { + private Map map = emptyMap(); + private boolean global = false; + + public Builder setMap(Map map) { + this.map = map; + return this; + } + + public Builder setGlobal(boolean global) { + this.global = global; + return this; + } + + public RecognizerOptions build() { + return new RecognizerOptions(this); + } + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/parsetree/AmbiguityNode.java b/benchmarks/src/main/kotlin/org/iguana/parsetree/AmbiguityNode.java new file mode 100644 index 000000000..26e488ec4 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/parsetree/AmbiguityNode.java @@ -0,0 +1,78 @@ +package org.iguana.parsetree; + +import org.iguana.grammar.runtime.RuntimeRule; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +import static java.util.Objects.hash; +import static java.util.Objects.requireNonNull; + +public class AmbiguityNode implements ParseTreeNode { + + private final Set alternatives; + + public AmbiguityNode(Set alternatives) { + this.alternatives = requireNonNull(alternatives); + } + + @Override + public int getStart() { + return alternatives.iterator().next().getStart(); + } + + @Override + public int getEnd() { + return alternatives.iterator().next().getEnd(); + } + + @Override + public String getName() { + return null; + } + + @Override + public List children() { + return new ArrayList<>(alternatives); + } + + @Override + public boolean hasChildren() { + return !alternatives.isEmpty(); + } + + @Override + public List accept(ParseTreeVisitor visitor) { + return visitor.visitAmbiguityNode(this); + } + + @Override + public RuntimeRule getGrammarDefinition() { + return null; + } + + @Override + public String getText() { + if (alternatives.size() == 0) return ""; + return children().get(0).getText(); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (!(obj instanceof AmbiguityNode)) return false; + AmbiguityNode other = (AmbiguityNode) obj; + return alternatives.equals(other.alternatives); + } + + @Override + public int hashCode() { + return hash(alternatives); + } + + @Override + public String toString() { + return "{" + alternatives.toString() + "}"; + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/parsetree/DefaultParseTreeBuilder.java b/benchmarks/src/main/kotlin/org/iguana/parsetree/DefaultParseTreeBuilder.java new file mode 100644 index 000000000..23c187ae6 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/parsetree/DefaultParseTreeBuilder.java @@ -0,0 +1,89 @@ +package org.iguana.parsetree; + +import org.iguana.grammar.runtime.RuntimeRule; +import org.iguana.grammar.slot.TerminalNodeType; +import org.iguana.grammar.symbol.Alt; +import org.iguana.grammar.symbol.Group; +import org.iguana.grammar.symbol.Opt; +import org.iguana.grammar.symbol.Plus; +import org.iguana.grammar.symbol.Star; +import org.iguana.grammar.symbol.Start; +import org.iguana.grammar.symbol.Terminal; +import org.iguana.utils.input.Input; + +import java.util.List; +import java.util.Set; + +import static org.iguana.parsetree.MetaSymbolNode.AltNode; +import static org.iguana.parsetree.MetaSymbolNode.GroupNode; +import static org.iguana.parsetree.MetaSymbolNode.OptionNode; +import static org.iguana.parsetree.MetaSymbolNode.PlusNode; +import static org.iguana.parsetree.MetaSymbolNode.StarNode; +import static org.iguana.parsetree.MetaSymbolNode.StartNode; + +public class DefaultParseTreeBuilder implements ParseTreeBuilder { + + private final Input input; + + public DefaultParseTreeBuilder(Input input) { + this.input = input; + } + + @Override + public TerminalNode terminalNode(Terminal terminal, int leftExtent, int rightExtent) { + if (terminal == Terminal.epsilon()) return null; + if (terminal.getNodeType() == TerminalNodeType.Literal) { + return new KeywordTerminalNode(terminal, leftExtent, rightExtent); + } + return new DefaultTerminalNode(terminal, leftExtent, rightExtent, input); + } + + @Override + public NonterminalNode nonterminalNode( + RuntimeRule rule, + List children, + int leftExtent, + int rightExtent) { + return new NonterminalNode(rule, children, leftExtent, rightExtent); + } + + @Override + public AmbiguityNode ambiguityNode(Set ambiguities) { + return new AmbiguityNode(ambiguities); + } + + @Override + public ParseTreeNode starNode(Star symbol, List children, int leftExtent, int rightExtent) { + return new StarNode(symbol, children, leftExtent, rightExtent); + } + + @Override + public ParseTreeNode plusNode(Plus symbol, List children, int leftExtent, int rightExtent) { + return new PlusNode(symbol, children, leftExtent, rightExtent); + } + + @Override + public ParseTreeNode optNode(Opt symbol, ParseTreeNode child, int leftExtent, int rightExtent) { + return new OptionNode(symbol, child, leftExtent, rightExtent); + } + + @Override + public ParseTreeNode altNode(Alt symbol, ParseTreeNode child, int leftExtent, int rightExtent) { + return new AltNode(symbol, child, leftExtent, rightExtent); + } + + @Override + public ParseTreeNode groupNode(Group symbol, List children, int leftExtent, int rightExtent) { + return new GroupNode(symbol, children, leftExtent, rightExtent); + } + + @Override + public ParseTreeNode startNode(Start symbol, List children, int leftExtent, int rightExtent) { + return new StartNode(symbol, children, leftExtent, rightExtent); + } + + @Override + public ParseTreeNode errorNode(int leftExtent, int rightExtent) { + return new ErrorNode(leftExtent, rightExtent, input); + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/parsetree/DefaultTerminalNode.java b/benchmarks/src/main/kotlin/org/iguana/parsetree/DefaultTerminalNode.java new file mode 100644 index 000000000..9706f65af --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/parsetree/DefaultTerminalNode.java @@ -0,0 +1,24 @@ +package org.iguana.parsetree; + +import org.iguana.grammar.symbol.Terminal; +import org.iguana.utils.input.Input; + +public class DefaultTerminalNode extends TerminalNode { + + private final Input input; + + public DefaultTerminalNode(Terminal terminal, int start, int end, Input input) { + super(terminal, start, end); + this.input = input; + } + + @Override + public String getText() { + return input.subString(getStart(), getEnd()); + } + + @Override + public boolean hasChildren() { + return false; + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/parsetree/ErrorNode.java b/benchmarks/src/main/kotlin/org/iguana/parsetree/ErrorNode.java new file mode 100644 index 000000000..a280302c7 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/parsetree/ErrorNode.java @@ -0,0 +1,73 @@ +package org.iguana.parsetree; + +import org.iguana.utils.input.Input; + +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +public class ErrorNode implements ParseTreeNode { + + private final Input input; + private final int start; + private final int end; + + public ErrorNode(int start, int end, Input input) { + this.start = start; + this.end = end; + this.input = input; + } + + @Override + public int getStart() { + return start; + } + + @Override + public int getEnd() { + return end; + } + + @Override + public String getName() { + return "Error"; + } + + @Override + public Object getGrammarDefinition() { + return null; + } + + @Override + public String getText() { + return input.subString(start, end); + } + + @Override + public Object accept(ParseTreeVisitor visitor) { + return visitor.visitErrorNode(this); + } + + @Override + public List children() { + return Collections.emptyList(); + } + + @Override + public boolean hasChildren() { + return false; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof ErrorNode)) return false; + ErrorNode errorNode = (ErrorNode) o; + return start == errorNode.start && end == errorNode.end; + } + + @Override + public int hashCode() { + return Objects.hash(start, end); + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/parsetree/KeywordTerminalNode.java b/benchmarks/src/main/kotlin/org/iguana/parsetree/KeywordTerminalNode.java new file mode 100644 index 000000000..b7671b14d --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/parsetree/KeywordTerminalNode.java @@ -0,0 +1,23 @@ +package org.iguana.parsetree; + +import org.iguana.grammar.symbol.Terminal; + +public class KeywordTerminalNode extends TerminalNode { + + public KeywordTerminalNode(Terminal terminal, int start, int end) { + super(terminal, start, end); + } + + @Override + public String getText() { + // Keyword names start and end with ', so here we have to strip them. + // TODO: document this somewhere + String name = getTerminal().getName(); + return name.substring(1, name.length() - 1); + } + + @Override + public boolean hasChildren() { + return false; + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/parsetree/MetaSymbolNode.java b/benchmarks/src/main/kotlin/org/iguana/parsetree/MetaSymbolNode.java new file mode 100644 index 000000000..772bbcf34 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/parsetree/MetaSymbolNode.java @@ -0,0 +1,195 @@ +package org.iguana.parsetree; + +import org.iguana.grammar.symbol.Alt; +import org.iguana.grammar.symbol.Group; +import org.iguana.grammar.symbol.Opt; +import org.iguana.grammar.symbol.Plus; +import org.iguana.grammar.symbol.Star; +import org.iguana.grammar.symbol.Start; +import org.iguana.grammar.symbol.Symbol; + +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +import static java.util.Objects.hash; +import static java.util.Objects.requireNonNull; + +public abstract class MetaSymbolNode implements ParseTreeNode { + + protected final Symbol symbol; + private final int start; + private final int end; + + public MetaSymbolNode(Symbol symbol, int start, int end) { + this.symbol = requireNonNull(symbol); + this.start = start; + this.end = end; + } + + @Override + public String getName() { + return symbol.getName(); + } + + @Override + public int getStart() { + return start; + } + + @Override + public int getEnd() { + return end; + } + + @Override + public Symbol getGrammarDefinition() { + return symbol; + } + + @Override + public String getText() { + StringBuilder sb = new StringBuilder(); + for (ParseTreeNode child : children()) { + sb.append(child.getText()); + } + return sb.toString(); + } + + @Override + public String toString() { + return String.format("(%s, %d, %d)", symbol, start, end); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof MetaSymbolNode)) return false; + MetaSymbolNode that = (MetaSymbolNode) o; + return Objects.equals(symbol, that.symbol) + && start == that.start + && end == that.end + && Objects.equals(children(), that.children()); + } + + @Override + public int hashCode() { + return hash(symbol, children(), start, end); + } + + + public abstract static class SingleChildMetaSymbolNode extends MetaSymbolNode { + + private final ParseTreeNode child; + + public SingleChildMetaSymbolNode(Symbol symbol, ParseTreeNode child, int start, int end) { + super(symbol, start, end); + this.child = child; + } + + @Override + public boolean hasChildren() { + return child != null; + } + + @Override + public List children() { + if (child == null) return Collections.emptyList(); + else return Collections.singletonList(child); + } + + /** + * Returns null when no child is present. + */ + public ParseTreeNode getChild() { + return child; + } + } + + public abstract static class MultiChildMetaSymbolNode extends MetaSymbolNode { + + private final List children; + + public MultiChildMetaSymbolNode(Symbol symbol, List children, int start, int end) { + super(symbol, start, end); + this.children = children == null ? Collections.emptyList() : children; + } + + @Override + public boolean hasChildren() { + return !children.isEmpty(); + } + + @Override + public List children() { + return children; + } + } + + public static class StarNode extends MultiChildMetaSymbolNode { + public StarNode(Star symbol, List children, int start, int end) { + super(symbol, children, start, end); + } + + @Override + public List accept(ParseTreeVisitor visitor) { + return visitor.visitStarNode(this); + } + } + + public static class PlusNode extends MultiChildMetaSymbolNode { + public PlusNode(Plus symbol, List children, int start, int end) { + super(symbol, children, start, end); + } + + @Override + public List accept(ParseTreeVisitor visitor) { + return visitor.visitPlusNode(this); + } + } + + public static class GroupNode extends MultiChildMetaSymbolNode { + public GroupNode(Group symbol, List children, int start, int end) { + super(symbol, children, start, end); + } + + @Override + public List accept(ParseTreeVisitor visitor) { + return visitor.visitGroupNode(this); + } + } + + public static class OptionNode extends SingleChildMetaSymbolNode { + public OptionNode(Opt symbol, ParseTreeNode child, int start, int end) { + super(symbol, child, start, end); + } + + @Override + public Optional accept(ParseTreeVisitor visitor) { + return visitor.visitOptionNode(this); + } + } + + public static class AltNode extends SingleChildMetaSymbolNode { + public AltNode(Alt symbol, ParseTreeNode child, int start, int end) { + super(symbol, child, start, end); + } + + @Override + public Object accept(ParseTreeVisitor visitor) { + return visitor.visitAltNode(this); + } + } + + public static class StartNode extends MultiChildMetaSymbolNode { + public StartNode(Start symbol, List children, int start, int end) { + super(symbol, children, start, end); + } + + @Override + public Object accept(ParseTreeVisitor visitor) { + return visitor.visitStartNode(this); + } + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/parsetree/NonterminalNode.java b/benchmarks/src/main/kotlin/org/iguana/parsetree/NonterminalNode.java new file mode 100644 index 000000000..650861e85 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/parsetree/NonterminalNode.java @@ -0,0 +1,92 @@ +package org.iguana.parsetree; + +import org.iguana.grammar.runtime.RuntimeRule; + +import java.util.Collections; +import java.util.List; + +import static java.util.Objects.hash; +import static java.util.Objects.requireNonNull; +import static org.iguana.utils.Assert.requireNonNegative; + +public class NonterminalNode implements ParseTreeNode { + + private final RuntimeRule rule; + private final int start; + private final int end; + private final List children; + + public NonterminalNode(RuntimeRule rule, List children, int start, int end) { + this.rule = requireNonNull(rule); + this.children = children == null ? Collections.emptyList() : children; + this.start = requireNonNegative(start); + this.end = requireNonNegative(end); + } + + @Override + public String getName() { + return rule.getHead().getName(); + } + + @Override + public int getStart() { + return start; + } + + @Override + public int getEnd() { + return end; + } + + @Override + public List children() { + return children; + } + + @Override + public boolean hasChildren() { + return !children.isEmpty(); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + return visitor.visitNonterminalNode(this); + } + + @Override + public RuntimeRule getGrammarDefinition() { + return rule; + } + + @Override + public String getText() { + if (children.size() == 1) return children.get(0).getText(); + + StringBuilder sb = new StringBuilder(); + for (ParseTreeNode child : children) { + sb.append(child.getText()); + } + return sb.toString(); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (!(obj instanceof NonterminalNode)) return false; + NonterminalNode other = (NonterminalNode) obj; + return this.start == other.start + && this.end == other.end + && this.rule.equals(other.rule) + && this.children.equals(other.children); + } + + @Override + public int hashCode() { + return hash(start, end, rule, children); + } + + @Override + public String toString() { + return String.format("(%s, %d, %d)", rule, start, end); + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/parsetree/ParseTreeBuilder.java b/benchmarks/src/main/kotlin/org/iguana/parsetree/ParseTreeBuilder.java new file mode 100644 index 000000000..13c4e96fd --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/parsetree/ParseTreeBuilder.java @@ -0,0 +1,58 @@ +package org.iguana.parsetree; + +import org.iguana.grammar.runtime.RuntimeRule; +import org.iguana.grammar.symbol.Alt; +import org.iguana.grammar.symbol.Group; +import org.iguana.grammar.symbol.Opt; +import org.iguana.grammar.symbol.Plus; +import org.iguana.grammar.symbol.Star; +import org.iguana.grammar.symbol.Start; +import org.iguana.grammar.symbol.Symbol; +import org.iguana.grammar.symbol.Terminal; + +import java.util.List; +import java.util.Set; + +public interface ParseTreeBuilder { + T terminalNode(Terminal terminal, int leftExtent, int rightExtent); + + T nonterminalNode(RuntimeRule rule, List children, int leftExtent, int rightExtent); + + T ambiguityNode(Set node); + + T starNode(Star symbol, List children, int leftExtent, int rightExtent); + + T plusNode(Plus symbol, List children, int leftExtent, int rightExtent); + + T optNode(Opt symbol, T child, int leftExtent, int rightExtent); + + T altNode(Alt symbol, T child, int leftExtent, int rightExtent); + + T groupNode(Group symbol, List children, int leftExtent, int rightExtent); + + T startNode(Start symbol, List children, int leftExtent, int rightExtent); + + T errorNode(int leftExtent, int rightExtent); + + default T metaSymbolNode(Symbol symbol, List children, int leftExtent, int rightExtent) { + if (symbol instanceof Star) { + return starNode((Star) symbol, children, leftExtent, rightExtent); + } + if (symbol instanceof Plus) { + return plusNode((Plus) symbol, children, leftExtent, rightExtent); + } + if (symbol instanceof Group) { + return groupNode((Group) symbol, children, leftExtent, rightExtent); + } + if (symbol instanceof Alt) { + return altNode((Alt) symbol, children.get(0), leftExtent, rightExtent); + } + if (symbol instanceof Opt) { + return optNode((Opt) symbol, children.isEmpty() ? null : children.get(0), leftExtent, rightExtent); + } + if (symbol instanceof Start) { + return startNode((Start) symbol, children, leftExtent, rightExtent); + } + throw new RuntimeException("Unknown meta symbol type: " + symbol); + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/parsetree/ParseTreeNode.java b/benchmarks/src/main/kotlin/org/iguana/parsetree/ParseTreeNode.java new file mode 100644 index 000000000..03ad5c5d3 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/parsetree/ParseTreeNode.java @@ -0,0 +1,28 @@ +package org.iguana.parsetree; + +import java.util.List; + +public interface ParseTreeNode { + + int getStart(); + + int getEnd(); + + String getName(); + + Object getGrammarDefinition(); + + String getText(); + + Object accept(ParseTreeVisitor visitor); + + List children(); + + boolean hasChildren(); + + default ParseTreeNode childAt(int i) { + if (i < 0 || i >= children().size()) + throw new IndexOutOfBoundsException(); + return children().get(i); + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/parsetree/ParseTreeVisitor.java b/benchmarks/src/main/kotlin/org/iguana/parsetree/ParseTreeVisitor.java new file mode 100644 index 000000000..2d3af0c16 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/parsetree/ParseTreeVisitor.java @@ -0,0 +1,135 @@ +package org.iguana.parsetree; + + +import org.iguana.grammar.symbol.Group; +import org.iguana.grammar.symbol.Opt; +import org.iguana.grammar.symbol.Plus; +import org.iguana.grammar.symbol.Star; +import org.iguana.grammar.symbol.Symbol; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +public interface ParseTreeVisitor { + + T visitNonterminalNode(NonterminalNode node); + + default List visitAmbiguityNode(AmbiguityNode node) { + throw new RuntimeException("Ambiguity"); + } + + default T visitTerminalNode(TerminalNode node) { + return null; + } + + default T visitErrorNode(ErrorNode node) { return null; } + + default List visitStarNode(MetaSymbolNode.StarNode node) { + return visitStarOrPlusNode(node); + } + + default List visitPlusNode(MetaSymbolNode.PlusNode node) { + return visitStarOrPlusNode(node); + } + + default Optional visitOptionNode(MetaSymbolNode.OptionNode node) { + if (node.children().size() == 0) { + return Optional.empty(); + } + return Optional.of((T) node.childAt(0).accept(this)); + } + + default List visitStartNode(MetaSymbolNode.StartNode node) { + return visitList(node.children()); + } + + default T visitAltNode(MetaSymbolNode.AltNode node) { + return (T) node.childAt(0).accept(this); + } + + default List visitGroupNode(MetaSymbolNode.GroupNode node) { + return visitList(node.children()); + } + + default List visitList(List children) { + int size = children.size(); + List result = new ArrayList<>(size); + for (int i = 0; i < size; i++) { + ParseTreeNode child = children.get(i); + T childResult = (T) child.accept(this); + if (childResult != null) { + result.add(childResult); + } + } + return result; + } + + default List visitStarOrPlusNode(MetaSymbolNode node) { + if (node.children().size() == 0) { + return Collections.emptyList(); + } + + Symbol symbol = node.getGrammarDefinition(); + + if (getSymbol(symbol) instanceof Group) { + int size = node.children().size(); + List result = new ArrayList<>(size); + for (int i = 0; i < size; i++) { + ParseTreeNode child = node.childAt(i); + List childResult = (List) child.accept(this); + // This can happen when we have lists with separators, e.g., {A ','}* + if (childResult != null) { + result.addAll(childResult); + } + } + return result; + } + + List result = new ArrayList<>(node.children().size()); + for (int i = 0; i < node.children().size(); i++) { + ParseTreeNode child = node.childAt(i); + T childResult = (T) child.accept(this); + if (childResult != null) { + result.add(childResult); + } + } + + return result; + } + + default List visitChildren(ParseTreeNode node) { + int size = node.children().size(); + + List result = new ArrayList<>(size); + + for (int i = 0; i < size; i++) { + ParseTreeNode child = node.childAt(i); + U childResult = (U) child.accept(this); + if (childResult != null) { + if (childResult instanceof List) { + result.addAll((List) childResult); + } else { + result.add(childResult); + } + } + } + + if (result.isEmpty()) { + return Collections.emptyList(); + } + + return result; + } + + static Symbol getSymbol(Symbol symbol) { + if (symbol instanceof Star) { + return ((Star) symbol).getSymbol(); + } else if (symbol instanceof Plus) { + return ((Plus) symbol).getSymbol(); + } else if (symbol instanceof Opt) { + return ((Opt) symbol).getSymbol(); + } else throw new RuntimeException("Unsupported symbol " + symbol); + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/parsetree/TerminalNode.java b/benchmarks/src/main/kotlin/org/iguana/parsetree/TerminalNode.java new file mode 100644 index 000000000..fd5a69e55 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/parsetree/TerminalNode.java @@ -0,0 +1,78 @@ +package org.iguana.parsetree; + +import org.iguana.grammar.symbol.Terminal; + +import java.util.Collections; +import java.util.List; + +import static java.util.Objects.hash; +import static java.util.Objects.requireNonNull; +import static org.iguana.utils.Assert.requireNonNegative; + +public abstract class TerminalNode implements ParseTreeNode { + + private final Terminal terminal; + private final int start; + private final int end; + + public TerminalNode(Terminal terminal, int start, int end) { + this.terminal = requireNonNull(terminal); + this.start = requireNonNegative(start); + this.end = requireNonNegative(end); + } + + @Override + public int getStart() { + return start; + } + + @Override + public int getEnd() { + return end; + } + + @Override + public String getName() { + return terminal.getName(); + } + + @Override + public List children() { + return Collections.emptyList(); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + return visitor.visitTerminalNode(this); + } + + @Override + public Terminal getGrammarDefinition() { + return terminal; + } + + public Terminal getTerminal() { + return terminal; + } + + @Override + public abstract String getText(); + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (!(obj instanceof TerminalNode)) return false; + TerminalNode other = (TerminalNode) obj; + return terminal.equals(other.terminal) && start == other.start && end == other.end; + } + + @Override + public int hashCode() { + return hash(terminal, start, end); + } + + @Override + public String toString() { + return String.format("(%s, %d, %d)", terminal.getName(), start, end); + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/parsetree/VisitResult.java b/benchmarks/src/main/kotlin/org/iguana/parsetree/VisitResult.java new file mode 100644 index 000000000..e90c29b0a --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/parsetree/VisitResult.java @@ -0,0 +1,539 @@ +package org.iguana.parsetree; + +import org.iguana.grammar.runtime.RuntimeRule; +import org.iguana.grammar.symbol.Symbol; +import org.iguana.sppf.PackedNode; +import org.iguana.utils.collections.CollectionsUtil; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.Set; + +public abstract class VisitResult { + + public abstract MergeResultVisitor visitor(); + + public abstract VisitResult merge(VisitResult other); + + public abstract T accept(CreateNodeVisitor visitor, PackedNode packedNode); + + public abstract java.util.List getValues(); + + public static Empty empty() { + return new VisitResult.Empty(); + } + + public static Single single(Object value) { + return new VisitResult.Single(value); + } + + public static List list(java.util.List values) { + return new VisitResult.List(values); + } + + public static EBNF ebnf(java.util.List values, Symbol symbol) { + return new EBNF(values, symbol); + } + + public static class Empty extends VisitResult { + + @Override + public MergeResultVisitor visitor() { + return new EmptyVisitor(); + } + + @Override + public VisitResult merge(VisitResult other) { + return other; + } + + @Override + public T accept(CreateNodeVisitor visitor, PackedNode packedNode) { + return visitor.visit(this, packedNode); + } + + @Override + public java.util.List getValues() { + return Collections.emptyList(); + } + + @Override + public String toString() { + return "[]"; + } + } + + public static class Single extends VisitResult { + + private final Object value; + + public Single(Object value) { + this.value = value; + } + + public Object getValue() { + return value; + } + + public VisitResult merge(VisitResult other) { + return (VisitResult) other.visitor().visit(this); + } + + @Override + public T accept(CreateNodeVisitor visitor, PackedNode packedNode) { + return visitor.visit(this, packedNode); + } + + @Override + public java.util.List getValues() { + java.util.List list = new ArrayList<>(1); + list.add(value); + return list; + } + + public MergeResultVisitor visitor() { + return new SingleVisitor(this); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (!(obj instanceof Single)) return false; + Single other = (Single) obj; + return value.equals(other.value); + } + + @Override + public String toString() { + return "[" + value.toString() + "]"; + } + } + + public static class List extends VisitResult { + + private final java.util.List values; + + public List(java.util.List values) { + this.values = values; + } + + public java.util.List getValues() { + return values; + } + + public VisitResult merge(VisitResult other) { + return (VisitResult) other.visitor().visit(this); + } + + @Override + public T accept(CreateNodeVisitor visitor, PackedNode packedNode) { + return visitor.visit(this, packedNode); + } + + @Override + public MergeResultVisitor visitor() { + return new ListVisitor(this); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (!(obj instanceof List)) return false; + List other = (List) obj; + return values.equals(other.values); + } + + @Override + public String toString() { + return values.toString(); + } + } + + public static class EBNF extends VisitResult { + + private final java.util.List values; + private final Symbol symbol; + + EBNF(java.util.List values, Symbol symbol) { + this.values = values; + this.symbol = symbol; + } + + public VisitResult merge(VisitResult other) { + return (VisitResult) other.visitor().visit(this); + } + + @Override + public T accept(CreateNodeVisitor visitor, PackedNode packedNode) { + return visitor.visit(this, packedNode); + } + + @Override + public java.util.List getValues() { + return values; + } + + @Override + public MergeResultVisitor visitor() { + return new EBNFResultVisitor(this); + } + + public Symbol getSymbol() { + return symbol; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (!(obj instanceof EBNF)) return false; + EBNF other = (EBNF) obj; + return values.equals(other.values); + } + + @Override + public String toString() { + return values.toString(); + } + } + + public static class ListOfResult extends VisitResult { + + private final java.util.List values; + + public ListOfResult(java.util.List values) { + this.values = values; + } + + @Override + public MergeResultVisitor visitor() { + return new ListOfResultVisitor(this); + } + + @Override + public VisitResult merge(VisitResult other) { + return (VisitResult) other.visitor().visit(this); + } + + @Override + public T accept(CreateNodeVisitor visitor, PackedNode packedNode) { + return visitor.visit(this, packedNode); + } + + @Override + public java.util.List getValues() { + throw new UnsupportedOperationException(); + } + + public java.util.List getVisitResults() { + return values; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (!(obj instanceof ListOfResult)) return false; + ListOfResult other = (ListOfResult) obj; + return values.equals(other.values); + } + + @Override + public String toString() { + return values.toString(); + } + } + + interface MergeResultVisitor { + Object visit(Empty other); + + Object visit(Single other); + + Object visit(List other); + + Object visit(EBNF other); + + Object visit(ListOfResult other); + } + + static class SingleVisitor implements MergeResultVisitor { + + private final Single result; + + SingleVisitor(Single result) { + this.result = result; + } + + @Override + public VisitResult visit(Empty empty) { + return result; + } + + @Override + public VisitResult visit(Single other) { + java.util.List values = new ArrayList<>(); + values.add(other.value); + values.add(result.value); + return new List(values); + } + + @Override + public VisitResult visit(List other) { + java.util.List values = new ArrayList<>(); + values.addAll(other.values); + values.add(result.value); + return new List(values); + } + + @Override + public EBNF visit(EBNF other) { + java.util.List values = new ArrayList<>(other.values); + values.add(result.value); + return new EBNF(values, other.symbol); + } + + @Override + public VisitResult visit(ListOfResult other) { + java.util.List visitResults = new ArrayList<>(); + for (VisitResult otherValue : other.values) { + java.util.List values = new ArrayList<>(); + values.addAll(otherValue.getValues()); + values.add(result.value); + visitResults.add(new List(values)); + } + return new ListOfResult(visitResults); + } + } + + static class ListVisitor implements MergeResultVisitor { + + private final List result; + + ListVisitor(List result) { + this.result = result; + } + + @Override + public List visit(Empty empty) { + return result; + } + + @Override + public List visit(Single other) { + java.util.List values = new ArrayList<>(); + values.add(other.value); + values.addAll(result.values); + return new List(values); + } + + @Override + public ListOfResult visit(List other) { + return new ListOfResult(CollectionsUtil.list(other, result)); + } + + @Override + public VisitResult visit(EBNF result) { + throw new RuntimeException("Combination is not possible"); + } + + @Override + public ListOfResult visit(ListOfResult other) { + java.util.List visitResults = new ArrayList<>(); + visitResults.addAll(other.values); + visitResults.add(result); + return new ListOfResult(visitResults); + } + } + + static class EBNFResultVisitor implements MergeResultVisitor { + + private final EBNF result; + + EBNFResultVisitor(EBNF result) { + this.result = result; + } + + @Override + public EBNF visit(Empty empty) { + return result; + } + + @Override + public List visit(Single other) { + java.util.List values = new ArrayList<>(2); + values.add(other.value); + values.add(result); + return new List(values); + } + + @Override + public VisitResult visit(List other) { + java.util.List values = new ArrayList<>(other.values.size() + 1); + values.addAll(other.values); + values.add(result); + return new List(values); + } + + @Override + public VisitResult visit(EBNF other) { + throw new RuntimeException("Combination is not possible"); + } + + @Override + public VisitResult visit(ListOfResult other) { + throw new RuntimeException("Combination is not possible"); + } + } + + static class ListOfResultVisitor implements MergeResultVisitor { + + private final ListOfResult result; + + ListOfResultVisitor(ListOfResult result) { + this.result = result; + } + + @Override + public ListOfResult visit(Empty empty) { + return result; + } + + @Override + public VisitResult visit(Single other) { + java.util.List visitResults = new ArrayList<>(); + for (VisitResult otherValue : result.values) { + java.util.List values = new ArrayList<>(); + values.add(other.value); + values.addAll(otherValue.getValues()); + visitResults.add(new List(values)); + } + return new ListOfResult(visitResults); + } + + @Override + public VisitResult visit(List other) { + java.util.List visitResults = new ArrayList<>(); + visitResults.add(other); + visitResults.addAll(result.values); + return new ListOfResult(visitResults); + } + + @Override + public VisitResult visit(EBNF other) { + throw new RuntimeException("Combination is not possible"); + } + + @Override + public VisitResult visit(ListOfResult other) { + java.util.List visitResults = new ArrayList<>(); + for (VisitResult otherValue : other.values) { + for (VisitResult thisValue : result.values) { + java.util.List values = new ArrayList<>(); + values.addAll(otherValue.getValues()); + values.addAll(thisValue.getValues()); + if (values.size() > 1) + visitResults.add(new List(values)); + else + visitResults.add(new Single(values.get(0))); + } + } + return new ListOfResult(visitResults); + } + } + + static class EmptyVisitor implements MergeResultVisitor { + + @Override + public VisitResult visit(Empty other) { + return other; + } + + @Override + public VisitResult visit(Single other) { + return other; + } + + @Override + public VisitResult visit(List other) { + return other; + } + + @Override + public VisitResult visit(EBNF other) { + return other; + } + + @Override + public VisitResult visit(ListOfResult other) { + return other; + } + } + + public interface CreateNodeVisitor { + T visit(Empty result, PackedNode packedNode); + + T visit(Single result, PackedNode packedNode); + + T visit(List result, PackedNode packedNode); + + T visit(EBNF result, PackedNode packedNode); + + T visit(ListOfResult result, PackedNode packedNode); + } + + + public static class CreateParseTreeVisitor implements CreateNodeVisitor> { + + private final ParseTreeBuilder parseTreeBuilder; + + public CreateParseTreeVisitor(ParseTreeBuilder parseTreeBuilder) { + this.parseTreeBuilder = parseTreeBuilder; + } + + @Override + public java.util.List visit(Empty result, PackedNode packedNode) { + RuntimeRule rule = packedNode.getGrammarSlot().getRule(); + return CollectionsUtil.list(parseTreeBuilder.nonterminalNode(rule, (java.util.List) result.getValues(), + packedNode.getLeftExtent(), packedNode.getRightExtent())); + } + + @Override + public java.util.List visit(Single result, PackedNode packedNode) { + RuntimeRule rule = packedNode.getGrammarSlot().getRule(); + return CollectionsUtil.list((parseTreeBuilder.nonterminalNode(rule, (java.util.List) result.getValues(), + packedNode.getLeftExtent(), packedNode.getRightExtent()))); + } + + @Override + public java.util.List visit(List result, PackedNode packedNode) { + java.util.List values = new ArrayList<>(); + for (Object o : result.getValues()) { + if (o instanceof VisitResult) { + values.addAll(((VisitResult) o).accept(this, packedNode)); + } else { + values.add((T) o); + } + } + RuntimeRule rule = packedNode.getGrammarSlot().getRule(); + return CollectionsUtil.list(parseTreeBuilder.nonterminalNode(rule, values, packedNode.getLeftExtent(), + packedNode.getRightExtent())); + } + + @Override + public java.util.List visit(EBNF result, PackedNode packedNode) { + T ebnfNode = parseTreeBuilder.metaSymbolNode(result.getSymbol(), (java.util.List) result.getValues(), + packedNode.getLeftExtent(), packedNode.getRightExtent()); + return CollectionsUtil.list(ebnfNode); + } + + @Override + public java.util.List visit(ListOfResult result, PackedNode packedNode) { + Set set = new LinkedHashSet<>(); + for (VisitResult vResult : result.getVisitResults()) { + set.add(parseTreeBuilder.nonterminalNode(packedNode.getGrammarSlot().getRule(), + (java.util.List) vResult.getValues(), packedNode.getLeftExtent(), packedNode.getRightExtent())); + } + return CollectionsUtil.list(parseTreeBuilder.ambiguityNode(set)); + } + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/regex/AbstractRegularExpression.java b/benchmarks/src/main/kotlin/org/iguana/regex/AbstractRegularExpression.java new file mode 100644 index 000000000..905465e0d --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/regex/AbstractRegularExpression.java @@ -0,0 +1,38 @@ +package org.iguana.regex; + +import org.iguana.regex.automaton.Automaton; +import org.iguana.regex.visitor.ToAutomatonRegexVisitor; + +import java.util.Set; + +public abstract class AbstractRegularExpression implements RegularExpression { + + private final Set lookaheads; + + private final Set lookbehinds; + + private Automaton automaton; + + public AbstractRegularExpression(RegexBuilder builder ) { + this.lookaheads = builder.lookaheads; + this.lookbehinds = builder.lookbehinds; + } + + @Override + public Set getLookaheads() { + return lookaheads; + } + + @Override + public Set getLookbehinds() { + return lookbehinds; + } + + public Automaton getAutomaton() { + if (automaton == null) { + automaton = accept(new ToAutomatonRegexVisitor()); + } + return automaton; + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/regex/Alt.java b/benchmarks/src/main/kotlin/org/iguana/regex/Alt.java new file mode 100644 index 000000000..4572f9715 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/regex/Alt.java @@ -0,0 +1,243 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.regex; + +import org.iguana.regex.visitor.RegularExpressionVisitor; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + +public class Alt extends AbstractRegularExpression implements Iterable { + + protected final List symbols; + + public Alt(Builder builder) { + super(builder); + this.symbols = builder.symbols; + } + + @SafeVarargs + @SuppressWarnings("varargs") + public static Alt from(T... symbols) { + return from(Arrays.asList(symbols)); + } + + public static Alt from(List list) { + return builder(list).build(); + } + + @Override + public boolean isNullable() { + return symbols.stream().anyMatch(e -> ((RegularExpression) e).isNullable()); + } + + @Override + public Iterator iterator() { + return symbols.iterator(); + } + + public int size() { + return symbols.size(); + } + + @Override + public int length() { + Optional max = symbols.stream().max(RegularExpression.lengthComparator()); + if (max.isPresent()) + return max.get().length(); + else + return 0; + } + + public T get(int index) { + return symbols.get(index); + } + + @Override + public boolean equals(Object obj) { + + if (obj == this) + return true; + + if (!(obj instanceof Alt)) + return false; + + Alt other = (Alt) obj; + + return other.symbols.equals(symbols); + } + + @Override + public int hashCode() { + return symbols.hashCode(); + } + + @Override + public String toString() { + return "(" + symbols.stream().map(a -> a.toString()).collect(Collectors.joining(" | ")) + ")"; + } + + @Override + public Set getFirstSet() { + return symbols.stream().flatMap(x -> x.getFirstSet().stream()).collect(Collectors.toSet()); + } + + @Override + public Set getNotFollowSet() { + return Collections.emptySet(); + } + + @Override + public Builder copy() { + return new Builder(this); + } + + public List getSymbols() { + return symbols; + } + + @Override + public List getChildren() { + return symbols; + } + + public static Alt not(RegularExpression... rs) { + return not(Arrays.asList(rs)); + } + + public static Alt not(List rs) { + List charRanges = new ArrayList<>(); + for (RegularExpression r : rs) { + if (r instanceof CharRange ) { + charRanges.add((CharRange) r); + } else if (r instanceof Char) { + charRanges.add(CharRange.in(((Char) r).getValue(), ((Char) r).getValue())); + } + else { + throw new RuntimeException("Can only be a character or a character range"); + } + } + return makeNot(charRanges); + } + + private static Alt makeNot(List ranges) { + List newRanges = new ArrayList<>(); + + int i = 0; + + Collections.sort(ranges); + + if (ranges.get(i).getStart() >= 1) { + newRanges.add(CharRange.in(1, ranges.get(i).getStart() - 1)); + } + + for (; i < ranges.size() - 1; i++) { + CharRange r1 = ranges.get(i); + CharRange r2 = ranges.get(i + 1); + + if (r2.getStart() > r1.getEnd() + 1) { + newRanges.add(CharRange.in(r1.getEnd() + 1, r2.getStart() - 1)); + } + } + + if (ranges.get(i).getEnd() < org.iguana.regex.CharacterRanges.MAX_UTF32_VAL) { + newRanges.add(CharRange.in(ranges.get(i).getEnd() + 1, CharacterRanges.MAX_UTF32_VAL)); + } + + return builder(newRanges).build(); + } + + public static Builder builder(T t1, T t2) { + return builder(Arrays.asList(t1, t2)); + } + + public static Builder builder(List symbols) { + return new Builder(symbols); + } + + @SafeVarargs + @SuppressWarnings("varargs") + public static Builder builder(T... symbols) { + return new Builder<>(Arrays.asList(symbols)); + } + + public static class Builder extends RegexBuilder> { + + private List symbols = new ArrayList<>(); + + public Builder() {} + + public Builder(List symbols) { + this.addAll(symbols); + } + + public Builder(Alt alt) { + super(alt); + this.addAll(alt.getSymbols()); + } + + public Builder add(T symbol) { + symbols.add(symbol); + return this; + } + + public Builder addAll(List symbols) { + this.symbols.addAll(symbols); + return this; + } + + public Builder setSymbols(List symbols) { + this.symbols = symbols; + return this; + } + + @Override + public RegexBuilder> setChildren(List children) { + this.symbols = (List) children; + return this; + } + + @Override + public Alt build() { + return new Alt<>(this); + } + + } + + @Override + public E accept(RegularExpressionVisitor visitor) { + return visitor.visit(this); + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/regex/Char.java b/benchmarks/src/main/kotlin/org/iguana/regex/Char.java new file mode 100644 index 000000000..bb75640b1 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/regex/Char.java @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.regex; + +import org.iguana.regex.visitor.RegularExpressionVisitor; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +/** + * + * @author Ali Afroozeh + * + */ +public class Char extends AbstractRegularExpression { + + private final int val; + + private Char(Builder builder) { + super(builder); + this.val = builder.val; + } + + public static Char from(int val) { + return new Builder(val).build(); + } + + public int getValue() { + return val; + } + + @Override + public int hashCode() { + return val; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + + if (!(obj instanceof Char)) + return false; + + Char other = (Char) obj; + + return val == other.val; + } + + public static String getName(int c) { + if (CharacterRanges.isPrintableAscii(c)) { + return (char) c + ""; + } else if (CharacterRanges.isKnownWhitespace(c)) { + return CharacterRanges.getKnownWhitespaceString(c); + } else { + String s = "\\u" + String.format("%04X", c); + // Escape newline inside strings + return s.equals("\\u000D") || s.equals("\\u000A") ? "\\" + s : s; + } + } + + @Override + public String toString() { + return "'" + getName(val) + "'"; + } + + @Override + public boolean isNullable() { + return false; + } + + @Override + public Set getFirstSet() { + Set firstSet = new HashSet<>(); + firstSet.add(CharRange.in(val, val)); + return firstSet; + } + + @Override + public Set getNotFollowSet() { + return Collections.emptySet(); + } + + @Override + public int length() { + return 1; + } + + public static Builder builder(int c) { + return new Builder(c); + } + + @Override + public RegexBuilder copy() { + return new Builder(this); + } + + public static class Builder extends RegexBuilder { + + private int val; + + private Builder() {} + + public Builder(int val) { + this.val = val; + } + + public Builder(Char character) { + super(character); + this.val = character.val; + } + + @Override + public Char build() { + return new Char(this); + } + } + + @Override + public T accept(RegularExpressionVisitor visitor) { + return visitor.visit(this); + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/regex/CharRange.java b/benchmarks/src/main/kotlin/org/iguana/regex/CharRange.java new file mode 100644 index 000000000..d12f37975 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/regex/CharRange.java @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.regex; + +import org.iguana.regex.visitor.RegularExpressionVisitor; +import org.iguana.utils.collections.hash.MurmurHash3; +import org.iguana.utils.collections.rangemap.Range; + +import java.util.Collections; +import java.util.Set; + +import static org.iguana.utils.collections.CollectionsUtil.immutableSet; + +/** + * + * @author Ali Afroozeh + * + */ +public class CharRange extends AbstractRegularExpression implements Range { + + private final int start; + + private final int end; + + private CharRange(Builder builder) { + super(builder); + + if (builder.end < builder.start) + throw new IllegalArgumentException("Start cannot be less than end."); + + this.start = builder.start; + this.end = builder.end; + } + + public static CharRange from(int c) { + return in(c, c); + } + + public static CharRange in(int start, int end) { + return new Builder(start, end).build(); + } + + public static String getName(int start, int end) { + if (start == end) { + return Char.getName(start); + } else { + return Char.getName(start) + "-" + Char.getName(end); + } + } + + @Override + public int getStart() { + return start; + } + + @Override + public int getEnd() { + return end; + } + + @Override + public int hashCode() { + return MurmurHash3.f2().apply(start, end); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + + if (!(obj instanceof CharRange)) + return false; + + CharRange other = (CharRange) obj; + + return start == other.start && end == other.end; + } + + @Override + public String toString() { + return getName(start, end); + } + + @Override + public boolean isNullable() { + return false; + } + + @Override + public Set getFirstSet() { + return immutableSet(this); + } + + @Override + public Set getNotFollowSet() { + return Collections.emptySet(); + } + + @Override + public int length() { + return 1; + } + + public static Builder builder(int start, int end) { + return new Builder(start, end); + } + + @Override + public Builder copy() { + return new Builder(this); + } + + public static class Builder extends RegexBuilder { + + private int start; + private int end; + + private Builder() {} + + public Builder(int start, int end) { + this.start = start; + this.end = end; + } + + public Builder(CharRange range) { + super(range); + this.start = range.start; + this.end = range.end; + } + + @Override + public CharRange build() { + return new CharRange(this); + } + } + + @Override + public T accept(RegularExpressionVisitor visitor) { + return visitor.visit(this); + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/regex/CharacterRanges.java b/benchmarks/src/main/kotlin/org/iguana/regex/CharacterRanges.java new file mode 100644 index 000000000..75efe83f5 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/regex/CharacterRanges.java @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.regex; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static org.iguana.utils.collections.CollectionsUtil.toList; + +public class CharacterRanges { + + public static final int MAX_UTF32_VAL = 0x10FFFF; + + public static Map> toNonOverlapping(Iterable ranges) { + return toNonOverlapping(toList(ranges)); + } + + public static Map> toNonOverlapping(CharRange... ranges) { + return toNonOverlapping(Arrays.asList(ranges)); + } + + public static Map> toNonOverlapping(List ranges) { + + if (ranges.size() == 0) + return Collections.emptyMap(); + + if (ranges.size() == 1) { + Map> map = new HashMap<>(); + map.computeIfAbsent(ranges.get(0), k -> new ArrayList<>()).add(ranges.get(0)); + return map; + } + + Collections.sort(ranges); + + Map> result = new HashMap<>(); + + Set overlapping = new HashSet<>(); + + for (int i = 0; i < ranges.size(); i++) { + + CharRange current = ranges.get(i); + overlapping.add(current); + + if (i + 1 < ranges.size()) { + CharRange next = ranges.get(i + 1); + if (!overlaps(overlapping, next)) { + result.putAll(convertOverlapping(overlapping)); + overlapping.clear(); + } + } + } + + result.putAll(convertOverlapping(overlapping)); + + return result; + } + + private static boolean overlaps(Set set, CharRange r) { + for (CharRange c : set) { + if (c.overlaps(r)) { + return true; + } + } + return false; + } + + private static Map> convertOverlapping(Set ranges) { + + if (ranges.isEmpty()) + return Collections.emptyMap(); + + Set set = new HashSet<>(); + for (CharRange r : ranges) { + set.add(r.getStart() - 1); + set.add(r.getEnd()); + } + List l = new ArrayList<>(set); + Collections.sort(l); + + List result = new ArrayList<>(); + + int start = l.get(0) + 1; + for (int i = 1; i < l.size(); i++) { + result.add(CharRange.in(start, l.get(i))); + start = l.get(i) + 1; + } + + Map> rangesMap = new HashMap<>(); + for (CharRange r1 : ranges) { + for (CharRange r2 : result) { + if (r1.contains(r2)) + rangesMap.computeIfAbsent(r1, k -> new ArrayList<>()).add(r2); + } + } + + return rangesMap; + } + + public static boolean isPrintableAscii(int codePoint) { + return '\u0020' < codePoint && codePoint < '\u007f'; + } + + public static boolean isKnownWhitespace(int codePoint) { + return codePoint == 9 || codePoint == 10 || codePoint == 12 || codePoint == 13 || codePoint == 20; + } + + public static String getKnownWhitespaceString(int codePoint) { + switch (codePoint) { + case 9: return "\\t"; + case 10: return "\\n"; + case 12: return "\\f"; + case 13: return "\\r"; + case 20: return "\\u0020"; + default: throw new RuntimeException("Unknown whitespace " + codePoint); + } + } + + public static Alt reverse(CharRange range) { + List ranges = new ArrayList<>(); + if (range.getStart() >= 1) { + ranges.add(CharRange.in(1, range.getStart() - 1)); + } + if (range.getEnd() < MAX_UTF32_VAL) { + ranges.add(CharRange.in(range.getEnd() + 1, MAX_UTF32_VAL)); + } + return Alt.from(ranges); + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/regex/EOF.java b/benchmarks/src/main/kotlin/org/iguana/regex/EOF.java new file mode 100644 index 000000000..b5b0ffb95 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/regex/EOF.java @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.regex; + +import org.iguana.regex.visitor.RegularExpressionVisitor; + +import java.util.Collections; +import java.util.Set; + +import static org.iguana.utils.collections.CollectionsUtil.immutableSet; + +public class EOF extends AbstractRegularExpression { + + public static int VALUE = -1; + + private static final Set firstSet = immutableSet(CharRange.in(VALUE, VALUE)); + + private static EOF instance; + + private EOF() { + super(new RegexBuilder() { + @Override + public EOF build() { + return EOF.getInstance(); + } + }); + } + + public static EOF getInstance() { + if (instance == null) { + instance = new EOF(); + } + return instance; + } + + public static CharRange asCharRange() { + return CharRange.in(VALUE, VALUE); + } + + @Override + public String toString() { + return "$"; + } + + @Override + public boolean isNullable() { + return false; + } + + @Override + public Set getFirstSet() { + return firstSet; + } + + @Override + public Set getNotFollowSet() { + return Collections.emptySet(); + } + + @Override + public int length() { + return 0; + } + + @Override + public RegexBuilder copy() { + throw new UnsupportedOperationException(); + } + + @Override + public T accept(RegularExpressionVisitor visitor) { + return visitor.visit(this); + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/regex/Epsilon.java b/benchmarks/src/main/kotlin/org/iguana/regex/Epsilon.java new file mode 100644 index 000000000..45ad78a06 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/regex/Epsilon.java @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.regex; + +import org.iguana.regex.visitor.RegularExpressionVisitor; + +import java.util.Collections; +import java.util.Set; + +import static org.iguana.utils.collections.CollectionsUtil.immutableSet; + +public class Epsilon extends AbstractRegularExpression { + + public static int VALUE = -2; + + private static final Set firstSet = immutableSet(CharRange.in(VALUE, VALUE)); + + private static Epsilon instance; + + private static final RegexBuilder builder = + new RegexBuilder() { + @Override + public Epsilon build() { + return Epsilon.getInstance(); + } + }; + + private Epsilon() { + super(builder); + } + + public static Epsilon getInstance() { + if (instance == null) + instance = new Epsilon(); + + return instance; + } + + public static CharRange asCharRange() { + return CharRange.in(VALUE, VALUE); + } + + @Override + public String toString() { + return "epsilon"; + } + + @Override + public boolean isNullable() { + return true; + } + + @Override + public Set getFirstSet() { + return firstSet; + } + + @Override + public Set getNotFollowSet() { + return Collections.emptySet(); + } + + @Override + public int length() { + return 0; + } + + @Override + public RegexBuilder copy() { + return builder; + } + + @Override + public T accept(RegularExpressionVisitor visitor) { + return visitor.visit(this); + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/regex/IguanaTokenizer.java b/benchmarks/src/main/kotlin/org/iguana/regex/IguanaTokenizer.java new file mode 100644 index 000000000..454613043 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/regex/IguanaTokenizer.java @@ -0,0 +1,79 @@ +package org.iguana.regex; + +import org.iguana.regex.automaton.Automaton; +import org.iguana.regex.automaton.AutomatonBuilder; +import org.iguana.regex.automaton.State; +import org.iguana.regex.matcher.DFAMatcher; +import org.iguana.utils.input.Input; + +import java.util.Map; + +public class IguanaTokenizer { + + private final DFAMatcher matcher; + private final Map regularExpressionCategories; + + private Input input; + private int inputIndex; + private Token nextToken; + + public IguanaTokenizer(Map regularExpressionCategories) { + this.regularExpressionCategories = regularExpressionCategories; + + int order = 0; + State startState = new State(); + State finalState = new State(); + for (RegularExpression regularExpression : regularExpressionCategories.keySet()) { + Automaton automaton = regularExpression.getAutomaton(); + for (State state : automaton.getFinalStates()) { + state.addRegularExpression(regularExpression, order++); + } + startState.addEpsilonTransition(automaton.getStartState()); + for (State automatonFinalState : automaton.getFinalStates()) { + automatonFinalState.addEpsilonTransition(finalState); + } + } + Automaton automaton = new AutomatonBuilder(startState).build(); + this.matcher = new DFAMatcher(automaton); + } + + public void prepare(Input input, int inputIndex) { + this.input = input; + this.inputIndex = inputIndex; + } + + public boolean hasNextToken() { + if (input == null) { + throw new IllegalStateException("The prepare method should be called first."); + } + if (nextToken != null) return true; + if (inputIndex >= input.length() - 1) return false; + int length = matcher.match(input, inputIndex); + if (length == 0) { + if (inputIndex == input.length() - 1) { + nextToken = new Token(EOF.getInstance(), EOF.getInstance().toString(), input, inputIndex, inputIndex); + return false; + } + throw new RuntimeException(); + } else if (length != -1) { + RegularExpression regularExpression = matcher.getMatchedRegularExpression(); + String category = regularExpressionCategories.get(regularExpression); + nextToken = new Token(regularExpression, category, input, inputIndex, inputIndex + length); + inputIndex = inputIndex + length; + return true; + } else { + nextToken = new Token(null, "Error", input, inputIndex, inputIndex + 1); + inputIndex++; + return true; + } + } + + public Token nextToken() { + if (nextToken != null) { + Token token = nextToken; + nextToken = null; + return token; + } + return null; + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/regex/InlineReferences.java b/benchmarks/src/main/kotlin/org/iguana/regex/InlineReferences.java new file mode 100644 index 000000000..f2619a8be --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/regex/InlineReferences.java @@ -0,0 +1,69 @@ +package org.iguana.regex; + +import org.iguana.regex.visitor.GatherReferencesVisitor; +import org.iguana.regex.visitor.InlineReferencesVisitor; +import org.iguana.regex.visitor.RegularExpressionVisitor; + +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; + +public class InlineReferences { + + public static Map inline(Map definitions) { + Map> referencesMap = new LinkedHashMap<>(); + for (Map.Entry entry : definitions.entrySet()) { + GatherReferencesVisitor gatherReferencesVisitor = new GatherReferencesVisitor(); + entry.getValue().accept(gatherReferencesVisitor); + referencesMap.put(entry.getKey(), gatherReferencesVisitor.getReferences()); + } + + for (List references : referencesMap.values()) { + for (String reference : references) { + if (!referencesMap.containsKey(reference)) { + throw new RuntimeException( + reference + " refers to a name which is not a defined regular expression"); + } + } + } + + isCyclic(referencesMap); + + Map current = definitions; + + while (true) { + RegularExpressionVisitor visitor = new InlineReferencesVisitor(current); + + Map newMap = new LinkedHashMap<>(); + for (Map.Entry entry : current.entrySet()) { + newMap.put(entry.getKey(), entry.getValue().accept(visitor)); + } + if (newMap.equals(current)) { + break; + } + current = newMap; + } + + return current; + } + + private static void isCyclic(Map> map) { + for (String node : map.keySet()) { + visit(node, map, new LinkedHashSet<>()); + } + } + + private static void visit(String node, Map> map, LinkedHashSet visited) { + if (visited.contains(node)) { + throw new RuntimeException("Regular expression references cannot be cyclic: " + + String.join("->", visited) + "->" + node); + } + + visited.add(node); + for (String neighbor : map.get(node)) { + visit(neighbor, map, visited); + } + visited.remove(node); + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/regex/Opt.java b/benchmarks/src/main/kotlin/org/iguana/regex/Opt.java new file mode 100644 index 000000000..b59c1a9a7 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/regex/Opt.java @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.regex; + +import org.iguana.regex.visitor.RegularExpressionVisitor; + +import java.util.Collections; +import java.util.List; +import java.util.Set; + +public class Opt extends AbstractRegularExpression { + + private final RegularExpression regex; + + private Opt(Builder builder) { + super(builder); + this.regex = builder.regex; + } + + public static Opt from(RegularExpression s) { + return builder(s).build(); + } + + public RegularExpression getSymbol() { + return regex; + } + + @Override + public int length() { + return regex.length(); + } + + @Override + public boolean isNullable() { + return true; + } + + @Override + public Set getFirstSet() { + return regex.getFirstSet(); + } + + @Override + public Set getNotFollowSet() { + return Collections.emptySet(); + } + + @Override + public List getChildren() { + return Collections.singletonList(regex); + } + + @Override + public Builder copy() { + return new Builder(regex); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + + if (!(obj instanceof Opt)) + return false; + + Opt other = (Opt) obj; + return regex.equals(other.regex); + } + + @Override + public int hashCode() { + return regex.hashCode(); + } + + @Override + public String toString() { + return regex.toString() + "?"; + } + + public static Builder builder(RegularExpression s) { + return new Builder(s); + } + + public static class Builder extends RegexBuilder { + + private RegularExpression regex; + + private Builder() {} + + public Builder(RegularExpression regex) { + this.regex = regex; + } + + public Builder(Opt opt) { + super(opt); + this.regex = opt.regex; + } + + public Builder setSymbol(RegularExpression regex) { + this.regex = regex; + return this; + } + + @Override + public RegexBuilder setChildren(List children) { + if (children.size() != 1) { + throw new RuntimeException("Children size should be one."); + } + this.regex = children.get(0); + return this; + } + + @Override + public Opt build() { + return new Opt(this); + } + } + + @Override + public T accept(RegularExpressionVisitor visitor) { + return visitor.visit(this); + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/regex/Plus.java b/benchmarks/src/main/kotlin/org/iguana/regex/Plus.java new file mode 100644 index 000000000..ba54e2a29 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/regex/Plus.java @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.regex; + +import org.iguana.regex.visitor.RegularExpressionVisitor; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Set; + +public class Plus extends AbstractRegularExpression { + + private final RegularExpression regex; + + private final List separators; + + private Plus(Builder builder) { + super(builder); + this.regex = builder.regex; + this.separators = Collections.unmodifiableList(builder.separators); + } + + public static Plus from(RegularExpression s) { + return builder(s).build(); + } + + @Override + public int length() { + return regex.length(); + } + + @Override + public boolean isNullable() { + return regex.isNullable(); + } + + @Override + public Set getFirstSet() { + return regex.getFirstSet(); + } + + @Override + public Set getNotFollowSet() { + return regex.getFirstSet(); + } + + public List getSeparators() { + return separators; + } + + @Override + public Builder copy() { + return new Builder(this); + } + + public RegularExpression getSymbol() { + return regex; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + + if (!(obj instanceof Plus)) + return false; + + Plus other = (Plus) obj; + return regex.equals(other.regex) && separators.equals(other.separators); + } + + @Override + public int hashCode() { + return regex.hashCode(); + } + + @Override + public String toString() { + return regex.toString() + "*"; + } + + public static Builder builder(RegularExpression s) { + return new Builder(s); + } + + public static class Builder extends org.iguana.regex.RegexBuilder { + + private RegularExpression regex; + + private List separators = new ArrayList<>(); + + private Builder() {} + + public Builder(RegularExpression regex) { + this.regex = regex; + } + + public Builder(Plus plus) { + super(plus); + this.regex = plus.regex; + this.separators = new ArrayList<>(plus.getSeparators()); + } + + @Override + public Plus build() { + return new Plus(this); + } + + public Builder setSymbol(RegularExpression regex) { + this.regex = regex; + return this; + } + + public Builder setSeparators(List separators) { + this.separators = separators; + return this; + } + + @Override + public RegexBuilder setChildren(List children) { + if (children.size() != 1) { + throw new RuntimeException("Children size should be one."); + } + this.regex = children.get(0); + return this; + } + } + + @Override + public T accept(RegularExpressionVisitor visitor) { + return visitor.visit(this); + } + + @Override + public List getChildren() { + return Collections.singletonList(regex); + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/regex/Reference.java b/benchmarks/src/main/kotlin/org/iguana/regex/Reference.java new file mode 100644 index 000000000..c26eb72c1 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/regex/Reference.java @@ -0,0 +1,92 @@ +package org.iguana.regex; + +import org.iguana.regex.visitor.RegularExpressionVisitor; + +import java.util.Objects; +import java.util.Set; + +public class Reference extends AbstractRegularExpression { + + private final String name; + + public Reference(Builder builder) { + super(builder); + this.name = builder.name; + } + + public static Reference from(String name) { + return new Builder(name).build(); + } + + public String getName() { + return name; + } + + @Override + public boolean isNullable() { + throw new UnsupportedOperationException(); + } + + @Override + public Set getFirstSet() { + throw new UnsupportedOperationException(); + } + + @Override + public Set getNotFollowSet() { + throw new UnsupportedOperationException(); + } + + @Override + public int length() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Reference)) return false; + Reference reference = (Reference) o; + return Objects.equals(name, reference.name); + } + + @Override + public int hashCode() { + return name.hashCode(); + } + + @Override + public T accept(RegularExpressionVisitor visitor) { + return visitor.visit(this); + } + + @Override + public RegexBuilder copy() { + return null; + } + + @Override + public String toString() { + return name; + } + + public static class Builder extends RegexBuilder { + + private String name; + + public Builder() { } + + Builder(String name) { + this.name = name; + } + + public void setName(String name) { + this.name = name; + } + + @Override + public Reference build() { + return new Reference(this); + } + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/regex/RegexBuilder.java b/benchmarks/src/main/kotlin/org/iguana/regex/RegexBuilder.java new file mode 100644 index 000000000..5ec50f491 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/regex/RegexBuilder.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.regex; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public abstract class RegexBuilder { + + protected Set lookaheads = new HashSet<>(); + + protected Set lookbehinds = new HashSet<>(); + + public RegexBuilder(T symbol) { + this.lookaheads = symbol.getLookaheads(); + this.lookbehinds = symbol.getLookbehinds(); + } + + public RegexBuilder() {} + + public RegexBuilder addLookbehind(org.iguana.regex.Char c) { + lookbehinds.add(org.iguana.regex.CharRange.from(c.getValue())); + return this; + } + + public RegexBuilder addLookbehind(org.iguana.regex.CharRange range) { + lookbehinds.add(range); + return this; + } + + public RegexBuilder addLookahead(Char c) { + lookaheads.add(org.iguana.regex.CharRange.from(c.getValue())); + return this; + } + + public RegexBuilder addLookahead(CharRange range) { + lookaheads.add(range); + return this; + } + + public RegexBuilder setChildren(List children) { + return this; + } + + public abstract T build(); + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/regex/RegularExpression.java b/benchmarks/src/main/kotlin/org/iguana/regex/RegularExpression.java new file mode 100644 index 000000000..7b1be8d4d --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/regex/RegularExpression.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.regex; + +import org.iguana.regex.automaton.Automaton; +import org.iguana.regex.visitor.RegularExpressionVisitor; + +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Set; + +public interface RegularExpression { + + boolean isNullable(); + + Set getLookaheads(); + + Set getLookbehinds(); + + Set getFirstSet(); + + /** + * The set of characters (ranges) that cannot follow this regular expressions. + */ + Set getNotFollowSet(); + + int length(); + + T accept(RegularExpressionVisitor visitor); + + Automaton getAutomaton(); + + default List getChildren() { + return Collections.emptyList(); + } + + RegexBuilder copy(); + + static Comparator lengthComparator() { + return (T o1, T o2) -> { + if (!(o1 instanceof RegularExpression) || !(o2 instanceof RegularExpression)) { return 0; } + + RegularExpression r1 = (RegularExpression) o1; + RegularExpression r2 = (RegularExpression) o2; + return r2.length() - r1.length(); + }; + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/regex/Seq.java b/benchmarks/src/main/kotlin/org/iguana/regex/Seq.java new file mode 100644 index 000000000..f9ec8146e --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/regex/Seq.java @@ -0,0 +1,207 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.regex; + +import org.iguana.regex.visitor.RegularExpressionVisitor; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +public class Seq extends AbstractRegularExpression implements Iterable { + + private final List symbols; + + private Seq(Builder builder) { + super(builder); + this.symbols = builder.symbols; + } + + public static Seq from(String s) { + return builder(s.chars().mapToObj(Char::from).collect(Collectors.toList())).build(); + } + + public static Seq from(int[] chars) { + return builder(Arrays.stream(chars).mapToObj(Char::from).collect(Collectors.toList())).build(); + } + + public static Seq from(List symbols) { + return builder(symbols).build(); + } + + @SafeVarargs + @SuppressWarnings("varargs") + public static Seq from(T... elements) { + return from(Arrays.asList(elements)); + } + + @Override + public int length() { + return symbols.stream().mapToInt(s -> s.length()).sum(); + } + + @Override + public boolean isNullable() { + return symbols.stream().allMatch(e -> e.isNullable()); + } + + public int size() { + return symbols.size(); + } + + public T get(int index) { + return symbols.get(index); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) + return true; + + if (!(obj instanceof Seq)) + return false; + + Seq other = (Seq) obj; + + return symbols.equals(other.symbols); + } + + @Override + public int hashCode() { + return symbols.hashCode(); + } + + @Override + public String toString() { + if (symbols.stream().allMatch(c -> c instanceof Char)) + return "'" + symbols.stream().map(c -> Char.getName(((Char) c).getValue())) + .collect(Collectors.joining("")) + "'"; + return "(" + symbols.stream().map(a -> a.toString()).collect(Collectors.joining(" ")) + ")"; + } + + @Override + public Iterator iterator() { + return symbols.iterator(); + } + + @Override + public Set getFirstSet() { + Set firstSet = new HashSet<>(); + for (RegularExpression regex : symbols) { + firstSet.addAll(regex.getFirstSet()); + if (!regex.isNullable()) { + break; + } + } + return firstSet; + } + + @Override + public Set getNotFollowSet() { + return Collections.emptySet(); + } + + + @Override + public Builder copy() { + return new Builder(this); + } + + public List getSymbols() { + return symbols; + } + + @Override + public List getChildren() { + return symbols; + } + + public static Builder builder(T s) { + return builder(Arrays.asList(s)); + } + + public static Builder builder(List symbols) { + return new Builder().setSymbols(symbols); + } + + @SafeVarargs + @SuppressWarnings("varargs") + public static Builder builder(T... symbols) { + return builder(Arrays.asList(symbols)); + } + + public static class Builder extends RegexBuilder> { + + private List symbols = new ArrayList<>(); + + public Builder(Seq seq) { + super(seq); + this.symbols = new ArrayList<>(seq.symbols); + } + + public Builder() {} + + public Builder add(T s) { + symbols.add(s); + return this; + } + + public Builder addAll(List symbols) { + this.symbols.addAll(symbols); + return this; + } + + public Builder setSymbols(List symbols) { + this.symbols = new ArrayList<>(symbols); + return this; + } + + @Override + public RegexBuilder> setChildren(List children) { + this.symbols = (List) children; + return this; + } + + @Override + public Seq build() { + return new Seq<>(this); + } + + } + + @Override + public E accept(RegularExpressionVisitor visitor) { + return visitor.visit(this); + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/regex/Star.java b/benchmarks/src/main/kotlin/org/iguana/regex/Star.java new file mode 100644 index 000000000..da49828a3 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/regex/Star.java @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.regex; + +import org.iguana.regex.visitor.RegularExpressionVisitor; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Set; + +public class Star extends AbstractRegularExpression { + + private final RegularExpression regex; + + private final List separators; + + private Star(Builder builder) { + super(builder); + this.regex = builder.regex; + this.separators = Collections.unmodifiableList(builder.separators); + } + + public static Star from(RegularExpression s) { + return builder(s).build(); + } + + @Override + public int length() { + return regex.length(); + } + + @Override + public T accept(RegularExpressionVisitor visitor) { + return visitor.visit(this); + } + + @Override + public boolean isNullable() { + return true; + } + + @Override + public Set getFirstSet() { + return regex.getFirstSet(); + } + + @Override + public Set getNotFollowSet() { + return regex.getFirstSet(); + } + + public List getSeparators() { + return separators; + } + + @Override + public Builder copy() { + return new Builder(this); + } + + public RegularExpression getSymbol() { + return regex; + } + + @Override + public List getChildren() { + return Collections.singletonList(regex); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + + if (!(obj instanceof Star)) + return false; + + Star other = (Star) obj; + return regex.equals(other.regex) && separators.equals(other.separators); + } + + @Override + public int hashCode() { + return regex.hashCode(); + } + + @Override + public String toString() { + return regex.toString() + "*"; + } + + public static Builder builder(RegularExpression s) { + return new Builder(s); + } + + public static class Builder extends RegexBuilder { + + private RegularExpression regex; + + private List separators = new ArrayList<>(); + + private Builder() { + regex = null; + } + + public Builder(RegularExpression regex) { + this.regex = regex; + } + + public Builder(Star star) { + super(star); + this.regex = star.regex; + this.setSeparators(star.getSeparators()); + } + + public Builder setSymbol(RegularExpression regex) { + this.regex = regex; + return this; + } + + public Builder setSeparators(List separators) { + this.separators = separators; + return this; + } + + @Override + public RegexBuilder setChildren(List children) { + if (children.size() != 1) { + throw new RuntimeException("Children size should be one."); + } + this.regex = children.get(0); + return this; + } + + @Override + public Star build() { + return new Star(this); + } + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/regex/Token.java b/benchmarks/src/main/kotlin/org/iguana/regex/Token.java new file mode 100644 index 000000000..4f84c3793 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/regex/Token.java @@ -0,0 +1,66 @@ +package org.iguana.regex; + +import org.iguana.utils.input.Input; + +import java.util.Objects; + +import static org.iguana.utils.string.StringUtil.escapeNewLine; + +public class Token { + + private final RegularExpression regularExpression; + private final String category; + private final Input input; + private final int start; + private final int end; + + public Token(RegularExpression regularExpression, String category, Input input, int start, int end) { + this.regularExpression = regularExpression; + this.category = category; + this.input = input; + this.start = start; + this.end = end; + } + + public String getLexeme() { + return input.subString(start, end); + } + + public int getStart() { + return start; + } + + public int getEnd() { + return end; + } + + public RegularExpression getRegularExpression() { + return regularExpression; + } + + public String getCategory() { + return category; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Token)) return false; + Token token = (Token) o; + return start == token.start + && end == token.end + && Objects.equals(regularExpression, token.regularExpression) + && Objects.equals(category, token.category) + && Objects.equals(input, token.input); + } + + @Override + public int hashCode() { + return Objects.hash(regularExpression, category, input, start, end); + } + + @Override + public String toString() { + return String.format("(%s, %d, %d, \"%s\")", category, start, end, escapeNewLine(getLexeme())); + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/regex/automaton/Automaton.java b/benchmarks/src/main/kotlin/org/iguana/regex/automaton/Automaton.java new file mode 100644 index 000000000..3831238a4 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/regex/automaton/Automaton.java @@ -0,0 +1,233 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.regex.automaton; + +import org.iguana.regex.CharRange; +import org.iguana.regex.RegularExpression; +import org.iguana.util.Tuple; + +import java.util.BitSet; +import java.util.Collection; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * + * @author Ali Afroozeh + * + */ +public class Automaton { + + private final State startState; + + private final State[] states; + + private final Set finalStates; + + private final boolean minimized; + + private final boolean deterministic; + + private final CharRange[] alphabet; + + public Automaton(AutomatonBuilder builder) { + this.startState = builder.getStartState(); + this.states = builder.getStates(); + this.finalStates = builder.getFinalStates(); + this.minimized = builder.isMinimized(); + this.deterministic = builder.isDeterministic(); + this.alphabet = builder.getAlphabet(); + } + + public State getStartState() { + return startState; + } + + public Set getFinalStates() { + return finalStates; + } + + public int getCountStates() { + return states.length; + } + + public State[] getStates() { + return states; + } + + public State getState(int id) { + return states[id]; + } + + public CharRange[] getAlphabet() { + return alphabet; + } + + public Collection getRegularExpressions() { + return finalStates.stream() + .flatMap(state -> state.getRegularExpressions().stream()) + .sorted(Comparator.comparing(Tuple::getSecond)) + .map(Tuple::getFirst) + .collect(Collectors.toList()); + } + + public boolean isDeterministic() { + return deterministic; + } + + public boolean isMinimized() { + return minimized; + } + + /** + * All characters accepted by this NFA. + */ + public BitSet getCharacters() { + return AutomatonBuilder.getCharacters(this); + } + + public Automaton copy() { + + final Map newStates = new HashMap<>(); + + final State[] newStartState = new State[1]; + + AutomatonVisitor.visit(startState, state -> { + State newState = new State(); + + newStates.put(state, newState); + newState.setStateType(state.getStateType()); + newState.addRegularExpressions(state.getRegularExpressions()); + + if (state == startState) { + newStartState[0] = newState; + } + }); + + AutomatonVisitor.visit(startState, state -> { + for (Transition transition : state.getTransitions()) { + State newState = newStates.get(state); + newState.addTransition(new Transition(transition.getStart(), + transition.getEnd(), + newStates.get(transition.getDestination()))); + } + }); + + return builder(newStartState[0]).build(); + } + + + /** + * Determines whether two NFAs are isomorphic. + * The NFAs are first made deterministic before performing the equality check. + */ +// @Override +// public boolean equals(Object obj) { +// +// if(obj == this) { +// return true; +// } +// +// if(!(obj instanceof Automaton)) { +// return false; +// } +// +// Automaton other = (Automaton) obj; +// +// Automaton thisNFA = AutomatonOperations.makeDeterministic(this); +// Automaton otherNFA = AutomatonOperations.makeDeterministic(other); +// +// Set visitedStates = new HashSet<>(); +// +// return isEqual(thisNFA.getStartState(), otherNFA.getStartState(), visitedStates); +// } + +// private boolean isEqual(State thisState, State otherState, Set visitedStates) { +// +// if(thisState.getCountTransitions() != otherState.getCountTransitions()) { +// return false; +// } +// +// int i = 0; +// Transition[] t1 = thisState.getSortedTransitions(); +// Transition[] t2 = otherState.getSortedTransitions(); +// while(i < thisState.getCountTransitions()) { +// if(t1[i].getStart() == t2[i].getStart() && t1[i].getEnd() == t2[i].getEnd()) { +// +// State d1 = t1[i].getDestination(); +// State d2 = t2[i].getDestination(); +// +// // Avoid infinite loop +// if(!(visitedStates.contains(d1) && visitedStates.contains(d2))) { +// visitedStates.add(d1); +// visitedStates.add(d2); +// if(!isEqual(d1, d2, visitedStates)) { +// return false; +// } +// } +// } +// i++; +// } +// +// return true; +// } + + /** + * Returns true if the language accepted by this automaton is empty. + */ + public boolean isLanguageEmpty() { + /* + * The final sates are calculated from the start state. This means that + * all final states returned by calling getFinalStates() are reachable. + * The language accepted by this automata is empty, if there are no reachable + * final states. + */ + + // Covers the case of the automaton for the empty regular expression + if (startState.getStateType() == StateType.FINAL) { + if (startState.getCountTransitions() == 1 + && startState.getTransitions().iterator().next().isEpsilonTransition()) { + return true; + } + } + + return getFinalStates().size() == 0; + } + + public AutomatonBuilder builder() { + return new AutomatonBuilder(this); + } + + public static AutomatonBuilder builder(State startState) { + return new AutomatonBuilder(startState); + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/regex/automaton/AutomatonBuilder.java b/benchmarks/src/main/kotlin/org/iguana/regex/automaton/AutomatonBuilder.java new file mode 100644 index 000000000..435e08780 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/regex/automaton/AutomatonBuilder.java @@ -0,0 +1,311 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.regex.automaton; + +import org.iguana.regex.CharRange; +import org.iguana.regex.CharacterRanges; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.BitSet; +import java.util.Collection; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +public class AutomatonBuilder { + + private State startState; + + private boolean deterministic; + + private boolean minimized; + + private CharRange[] alphabet; + + private State[] states; + + private Set finalStates; + + /** + * From transitions to non-overlapping transitions + */ + private Map> rangeMap; + + public AutomatonBuilder(Automaton automaton) { + this.deterministic = automaton.isDeterministic(); + this.minimized = automaton.isMinimized(); + this.states = automaton.getStates(); + this.startState = automaton.getStartState(); + this.finalStates = automaton.getFinalStates(); + this.alphabet = automaton.getAlphabet(); + + if (!deterministic) { + convertToNonOverlapping(automaton.getStartState()); + } + } + + public AutomatonBuilder(State startState) { + this.states = getAllStates(startState); + this.startState = startState; + this.finalStates = computeFinalStates(); + convertToNonOverlapping(startState); + } + + public Automaton build() { + setStateIDs(); + return new Automaton(this); + } + + public CharRange[] getAlphabet() { + return alphabet; + } + + public Set getFinalStates() { + return finalStates; + } + + public State getStartState() { + return startState; + } + + public State[] getStates() { + return states; + } + + public boolean isMinimized() { + return minimized; + } + + public boolean isDeterministic() { + return deterministic; + } + + private static Map> getRangeMap(State startState) { + return CharacterRanges.toNonOverlapping(getAllRanges(startState)); + } + + private static CharRange[] getAlphabet(Map> rangeMap) { + Set values = rangeMap.values().stream() + .flatMap(Collection::stream) + .collect(Collectors.toCollection(LinkedHashSet::new)); + + CharRange[] alphabet = new CharRange[values.size()]; + int i = 0; + for (CharRange r : values) { + alphabet[i++] = r; + } + return alphabet; + } + + public AutomatonBuilder makeDeterministic() { + Automaton dfa = AutomatonOperations.makeDeterministic(startState, alphabet); + this.startState = dfa.getStartState(); + this.finalStates = dfa.getFinalStates(); + this.states = dfa.getStates(); + this.deterministic = true; + return this; + } + + public AutomatonBuilder setDeterministic(boolean deterministic) { + this.deterministic = deterministic; + return this; + } + + /** + * + * Note: unreachable states are already removed as we gather the states + * reachable from the start state of the given NFA. + * + */ + public AutomatonBuilder minimize() { + if (!deterministic) + makeDeterministic(); + + Automaton automaton = AutomatonOperations.minimize(alphabet, states, startState); + this.startState = automaton.getStartState(); + this.finalStates = automaton.getFinalStates(); + this.states = automaton.getStates(); + this.minimized = true; + return this; + } + + public AutomatonBuilder setMinimized(boolean minimized) { + this.minimized = minimized; + return this; + } + + /** + * For debugging purposes + */ + @SuppressWarnings("unused") + private static void printMinimizationTable(int[][] table) { + for (int i = 0; i < table.length; i++) { + for (int j = 0; j < i; j++) { + System.out.print(table[i][j] + " "); + } + System.out.println("\n"); + } + } + + public static BitSet getCharacters(Automaton automaton) { + final BitSet bitSet = new BitSet(); + + AutomatonVisitor.visit(automaton.getStartState(), state -> { + for (Transition transition : state.getTransitions()) { + bitSet.set(transition.getStart(), transition.getEnd() + 1); + } + }); + + return bitSet; + } + + public static int[] merge(int[] i1, int[] i2) { + Set set = new HashSet<>(); + + for (int i = 0; i < i1.length; i++) { + set.add(i1[i]); + } + + for (int i = 0; i < i2.length; i++) { + set.add(i2[i]); + } + + int i = 0; + int[] merged = new int[set.size()]; + for (int c : set) { + merged[i++] = c; + } + + Arrays.sort(merged); + + return merged; + } + + private void convertToNonOverlapping(State startState) { + this.rangeMap = getRangeMap(startState); + for (State state : states) { + List removeList = new ArrayList<>(); + List addList = new ArrayList<>(); + for (Transition transition : state.getTransitions()) { + if (!transition.isEpsilonTransition()) { + removeList.add(transition); + for (CharRange range : rangeMap.get(transition.getRange())) { + addList.add(new Transition(range, transition.getDestination())); + } + } + } + state.removeTransitions(removeList); + state.addTransitions(addList); + } + this.alphabet = getAlphabet(rangeMap); + } + + private static List getAllRanges(State startState) { + final Set ranges = new HashSet<>(); + + AutomatonVisitor.visit(startState, state -> { + for (Transition transition : state.getTransitions()) { + if (!transition.isEpsilonTransition()) { + ranges.add(transition.getRange()); + } + } + }); + + return new ArrayList<>(ranges); + } + + public static int getCountStates(Automaton automaton) { + final int[] count = new int[1]; + + AutomatonVisitor.visit(automaton, s -> count[0]++); + + return count[0]; + } + + private static State[] getAllStates(State startState) { + final Set set = new LinkedHashSet<>(); + AutomatonVisitor.visit(startState, s -> set.add(s)); + + State[] states = new State[set.size()]; + int i = 0; + for (State s : set) { + states[i++] = s; + } + return states; + } + + public void setStateIDs() { + for (int i = 0; i < states.length; i++) { + states[i].setId(i); + } + } + + private Set computeFinalStates() { + + final Set finalStates = new HashSet<>(); + + AutomatonVisitor.visit(startState, s -> { + if (s.isFinalState()) { + finalStates.add(s); + } + }); + + return finalStates; + } + + + public State getState(int i) { + return states[i]; + } + + /** + * Makes the transition function complete, i.e., from each state + * there will be all outgoing transitions. + */ + public AutomatonBuilder makeComplete() { + + makeDeterministic(); + + State dummyState = new State(); + + AutomatonVisitor.visit(startState, s -> { + for (CharRange r : alphabet) { + if (!s.hasTransition(r)) { + s.addTransition(new Transition(r, dummyState)); + } + } + }); + + return this; + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/regex/automaton/AutomatonOperations.java b/benchmarks/src/main/kotlin/org/iguana/regex/automaton/AutomatonOperations.java new file mode 100644 index 000000000..82f93525b --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/regex/automaton/AutomatonOperations.java @@ -0,0 +1,479 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.regex.automaton; + +import org.iguana.regex.CharRange; +import org.iguana.regex.CharacterRanges; + +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Deque; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.stream.Collectors; + +public class AutomatonOperations { + + public static Automaton makeDeterministic(Automaton automaton) { + if (automaton.isDeterministic()) + return automaton; + + return makeDeterministic(automaton.getStartState(), automaton.getAlphabet()); + } + + public static Automaton makeDeterministic(State start, CharRange[] alphabet) { + + Set> visitedStates = new HashSet<>(); + Deque> processList = new ArrayDeque<>(); + + Set initialState = new HashSet<>(); + initialState.add(start); + initialState = epsilonClosure(initialState); + visitedStates.add(initialState); + processList.add(initialState); + + /* + * A map from the set of NFA states to the new state in the produced DFA. + * This map is used for sharing DFA states. + */ + Map, State> newStatesMap = new HashMap<>(); + + State startState = new State(); + + newStatesMap.put(initialState, startState); + + while (!processList.isEmpty()) { + Set stateSet = processList.poll(); + + for (CharRange r : alphabet) { + Set destState = move(stateSet, r); + + if (destState.isEmpty()) + continue; + + State source = newStatesMap.get(stateSet); + State dest = newStatesMap.computeIfAbsent(destState, s -> { + State state = new State(); + for (State ds : destState) { + state.addRegularExpressions(ds.getRegularExpressions()); + } + return state; + }); + source.addTransition(new Transition(r, dest)); + + if (!visitedStates.contains(destState)) { + visitedStates.add(destState); + processList.add(destState); + } + } + } + + // Setting the final states. + outer: + for (Entry, State> e : newStatesMap.entrySet()) { + for (State s : e.getKey()) { + if (s.getStateType() == StateType.FINAL) { + e.getValue().setStateType(StateType.FINAL); + continue outer; + } + } + } + + return Automaton.builder(startState).setDeterministic(true).build(); + } + + public static Automaton union(Automaton a1, Automaton a2) { + return op(a1, a2, (s1, s2) -> s1.isFinalState() || s2.isFinalState()); + } + + public static Automaton intersect(Automaton a1, Automaton a2) { + return op(a1, a2, (s1, s2) -> s1.isFinalState() && s2.isFinalState()); + } + + public static Automaton difference(Automaton a1, Automaton a2) { + return op(a1, a2, (s1, s2) -> s1.isFinalState() && !s2.isFinalState()); + } + + private static Automaton op(Automaton a1, Automaton a2, Op op) { + a1 = makeDeterministic(a1); + a2 = makeDeterministic(a2); + + Map> rangeMap = merge(a1.getAlphabet(), a2.getAlphabet()); + convertToNonOverlapping(a1, rangeMap); + convertToNonOverlapping(a2, rangeMap); + + Set values = rangeMap.values().stream() + .flatMap(List::stream) + .collect(Collectors.toSet()); + + a1 = makeComplete(a1, values); + a2 = makeComplete(a2, values); + + return product(a1, a2, values, op); + } + + /** + * Produces the Cartesian product of the states of an automata. + */ + private static Automaton product(Automaton a1, Automaton a2, Set values, Op op) { + + State[] states1 = a1.getStates(); + State[] states2 = a2.getStates(); + + State[][] newStates = new State[states1.length][states2.length]; + + State startState = null; + + for (int i = 0; i < states1.length; i++) { + for (int j = 0; j < states2.length; j++) { + + State state = getState(newStates, i, j); + State state1 = states1[i]; + State state2 = states2[j]; + + for (CharRange r : values) { + State dest1 = state1.getState(r); + State dest2 = state2.getState(r); + if (dest1 != null && dest2 != null) { + State dest = getState(newStates, dest1.getId(), dest2.getId()); + state.addTransition(new Transition(r, dest)); + } + } + + if (op.execute(state1, state2)) { + state.setStateType(StateType.FINAL); + } + + if (state1 == a1.getStartState() && state2 == a2.getStartState()) { + startState = state; + } + } + } + + return Automaton.builder(startState).build(); + } + + private static State getState(State[][] newStates, int i, int j) { + State state = newStates[i][j]; + if (state == null) { + state = new State(); + newStates[i][j] = state; + } + return state; + } + + public static void convertToNonOverlapping(Automaton a, Map> rangeMap) { + for (State state : a.getStates()) { + List removeList = new ArrayList<>(); + List addList = new ArrayList<>(); + for (Transition transition : state.getTransitions()) { + if (!transition.isEpsilonTransition()) { + removeList.add(transition); + for (CharRange range : rangeMap.get(transition.getRange())) { + addList.add(new Transition(range, transition.getDestination())); + } + } + } + state.removeTransitions(removeList); + state.addTransitions(addList); + } + } + + + public static Automaton makeComplete(Automaton automaton, Iterable alphabet) { + + State dummyState = new State(); + alphabet.forEach(r -> dummyState.addTransition(new Transition(r, dummyState))); + + for (State state : automaton.getStates()) { + for (CharRange r : alphabet) { + if (!state.hasTransition(r)) { + state.addTransition(new Transition(r, dummyState)); + } + } + } + + return Automaton.builder(automaton.getStartState()).build(); + } + + private static Map> merge(CharRange[] alphabet1, CharRange[] alphabet2) { + List alphabets = new ArrayList<>(); + for (CharRange r : alphabet1) { alphabets.add(r); } + for (CharRange r : alphabet2) { alphabets.add(r); } + + return CharacterRanges.toNonOverlapping(alphabets); + } + + + public static Automaton minimize(Automaton automaton) { + if (automaton.isMinimized()) + return automaton; + + return minimize(automaton.getAlphabet(), automaton.getStates(), automaton.getStartState()); + } + + + /** + * Creates the reverse of the given automaton. A reverse automaton + * accept the reverse language accepted by the original automaton. To construct + * a reverse automaton, all final states of the original automaton are becoming + * start states, transitions are reversed and the start state becomes the + * only final state. + * + */ + public static Automaton reverse(Automaton automaton) { + + // 1. Creating new states for each state of the original automaton + final Map newStates = new HashMap<>(); + + for (State s : automaton.getStates()) { + newStates.put(s, new State()); + } + + // 2. Creating a new start state and adding epsilon transitions to the final + // states of the original automata + State startState = new State(); + + for (State finalState : automaton.getFinalStates()) { + startState.addEpsilonTransition(newStates.get(finalState)); + } + + // 3. Reversing the transitions + for (State state : automaton.getStates()) { + for (Transition t : state.getTransitions()) { + newStates.get(t.getDestination()).addTransition(new Transition(t.getRange(), newStates.get(state))); + } + } + + // 4. Making the start state final + newStates.get(automaton.getStartState()).setStateType(StateType.FINAL); + + return Automaton.builder(startState).build(); + } + + /** + * + * Note: unreachable states are already removed as we gather the states + * reachable from the start state of the given NFA. + * + */ + public static Automaton minimize(CharRange[] alphabet, State[] states, State startState) { + + int size = states.length; + int[][] table = new int[size][size]; + + final int EMPTY = -2; + final int EPSILON = -1; + + for (int i = 0; i < table.length; i++) { + for (int j = 0; j < i; j++) { + table[i][j] = EMPTY; + } + } + + for (int i = 0; i < table.length; i++) { + for (int j = 0; j < i; j++) { + if (states[i].isFinalState() && !states[j].isFinalState()) { + table[i][j] = EPSILON; + } + if (states[j].isFinalState() && !states[i].isFinalState()) { + table[i][j] = EPSILON; + } + + // Differentiate between final states + if (states[i].isFinalState() + && states[j].isFinalState()) { + table[i][j] = EPSILON; + } + } + } + + boolean changed = true; + + while (changed) { + changed = false; + + for (int i = 0; i < table.length; i++) { + for (int j = 0; j < i; j++) { + + // If two states i and j are distinct + if (table[i][j] == EMPTY) { + for (int t = 0; t < alphabet.length; t++) { + State q1 = states[i].getState(alphabet[t]); + State q2 = states[j].getState(alphabet[t]); + + // If both states i and j have no outgoing transitions on the interval t, continue with + // the next transition. + if (q1 == null && q2 == null) { + continue; + } + + // If the transition t can be applied on state i but not on state j, two states are + // disjoint. Continue with the next pair of states. + if ((q1 == null && q2 != null) || (q2 == null && q1 != null)) { + table[i][j] = t; + changed = true; + break; + } + + if (q1.getId() == q2.getId()) { + continue; + } + + int a; + int b; + if (q1.getId() > q2.getId()) { + a = q1.getId(); + b = q2.getId(); + } else { + a = q2.getId(); + b = q1.getId(); + } + + if (table[a][b] != EMPTY) { + table[i][j] = t; + changed = true; + break; + } + } + } + } + } + } + + Map> partitionsMap = new HashMap<>(); + + for (int i = 0; i < table.length; i++) { + for (int j = 0; j < i; j++) { + if (table[i][j] == EMPTY) { + State stateI = states[i]; + State stateJ = states[j]; + + Set partitionI = partitionsMap.get(stateI); + Set partitionJ = partitionsMap.get(stateJ); + + if (partitionI == null && partitionJ == null) { + Set set = new HashSet<>(); + set.add(stateI); + set.add(stateJ); + partitionsMap.put(stateI, set); + partitionsMap.put(stateJ, set); + } + else if (partitionI == null && partitionJ != null) { + partitionJ.add(stateI); + partitionsMap.put(stateI, partitionJ); + } + else if (partitionJ == null && partitionI != null) { + partitionI.add(stateJ); + partitionsMap.put(stateJ, partitionI); + } + else { + partitionJ.addAll(partitionI); + partitionI.addAll(partitionJ); + } + } + } + } + + HashSet> partitions = new HashSet<>(partitionsMap.values()); + + State newStartState = null; + + for (State state : states) { + if (partitionsMap.get(state) == null) { + Set set = new HashSet<>(); + set.add(state); + partitions.add(set); + } + } + + Map newStates = new HashMap<>(); + + for (Set set : partitions) { + State newState = new State(); + for (State state : set) { + + newState.addRegularExpressions(state.getRegularExpressions()); + + if (set.contains(startState)) { + newStartState = newState; + } + if (state.isFinalState()) { + newState.setStateType(StateType.FINAL); + } + newStates.put(state, newState); + } + } + + for (State state : states) { + for (Transition t : state.getTransitions()) { + newStates.get(state).addTransition(new Transition(t.getStart(), t.getEnd(), + newStates.get(t.getDestination()))); + } + } + + return Automaton.builder(newStartState).build(); + } + + + private static Set epsilonClosure(Set states) { + Set newStates = new HashSet<>(states); + + for (State state : states) { + Set s = state.getEpsilonSates(); + if (!s.isEmpty()) { + newStates.addAll(s); + newStates.addAll(epsilonClosure(s)); + } + } + + return newStates; + } + + private static Set move(Set state, CharRange r) { + Set result = new HashSet<>(); + for (State s : state) { + State dest = s.getState(r); + if (dest != null) { + result.add(dest); + } + } + + return epsilonClosure(result); + } + + @FunctionalInterface + private interface Op { + boolean execute(State s1, State s2); + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/regex/automaton/AutomatonVisitor.java b/benchmarks/src/main/kotlin/org/iguana/regex/automaton/AutomatonVisitor.java new file mode 100644 index 000000000..a541f442a --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/regex/automaton/AutomatonVisitor.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.regex.automaton; + +import java.util.HashSet; +import java.util.Set; +import java.util.function.Consumer; + +/** + * A depth-first visitor for automaton + */ +public class AutomatonVisitor { + + public static void visit(Automaton automaton, Consumer action) { + Set visitedStates = new HashSet<>(); + visit(automaton.getStartState(), action, visitedStates); + } + + public static void visit(State state, Consumer action) { + Set visitedStates = new HashSet<>(); + visit(state, action, visitedStates); + } + + public static void visit(State state, Consumer action, Set visitedStates) { + action.accept(state); + visitedStates.add(state); + + for (Transition transition : state.getTransitions()) { + State destination = transition.getDestination(); + if (!visitedStates.contains(destination)) { + visit(destination, action, visitedStates); + } + } + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/regex/automaton/State.java b/benchmarks/src/main/kotlin/org/iguana/regex/automaton/State.java new file mode 100644 index 000000000..8ee10a937 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/regex/automaton/State.java @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.regex.automaton; + +import org.iguana.regex.CharRange; +import org.iguana.regex.RegularExpression; +import org.iguana.util.Tuple; + +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +public class State { + + private final Set transitions; + + private final Map reachableStates; + + private final Set epsilonSates; + + /** + * The set of regular expressions whose final state is this state. + */ + private final Set> regularExpressions; + + private StateType stateType; + + private int id; + + public State() { + this(StateType.NORMAL); + } + + public State(StateType stateType) { + this.transitions = new HashSet<>(); + this.reachableStates = new HashMap<>(); + this.stateType = stateType; + this.regularExpressions = new HashSet<>(); + this.epsilonSates = new HashSet<>(); + } + + public Set getTransitions() { + return transitions; + } + + public State getState(CharRange r) { + return reachableStates.get(r); + } + + public boolean hasTransition(CharRange r) { + return reachableStates.get(r) != null; + } + + public StateType getStateType() { + return stateType; + } + + public boolean isFinalState() { + return stateType == StateType.FINAL; + } + + public void setStateType(StateType stateType) { + this.stateType = stateType; + } + + public void addEpsilonTransition(State dest) { + transitions.add(new Transition(-1, dest)); + epsilonSates.add(dest); + } + + public void addTransitions(Iterable transitions) { + for (Transition t : transitions) { + addTransition(t); + } + } + + public void addTransition(Transition transition) { + if (transition.isEpsilonTransition()) + epsilonSates.add(transition.getDestination()); + + if (transitions.add(transition)) { + reachableStates.put(transition.getRange(), transition.getDestination()); + } + } + + public void removeTransition(Transition transition) { + transitions.remove(transition); + } + + public void removeTransitions(Collection c) { + transitions.removeAll(c); + } + + public void setId(int id) { + this.id = id; + } + + public int getId() { + return id; + } + + public void clearRegularExpressions() { + regularExpressions.clear(); + } + + public Set> getRegularExpressions() { + return regularExpressions; + } + + public Set getEpsilonSates() { + return epsilonSates; + } + + public State addRegularExpression(RegularExpression regex, int order) { + regularExpressions.add(Tuple.of(regex, order)); + return this; + } + + public State addRegularExpressions(Collection> c) { + regularExpressions.addAll(c); + return this; + } + + public int getCountTransitions() { + return transitions.size(); + } + + @Override + public String toString() { + return "State" + id; + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/regex/automaton/StateType.java b/benchmarks/src/main/kotlin/org/iguana/regex/automaton/StateType.java new file mode 100644 index 000000000..ac47172d8 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/regex/automaton/StateType.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.regex.automaton; + +public enum StateType { + NORMAL, + FINAL, +} diff --git a/benchmarks/src/main/kotlin/org/iguana/regex/automaton/Transition.java b/benchmarks/src/main/kotlin/org/iguana/regex/automaton/Transition.java new file mode 100644 index 000000000..c2d150e1a --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/regex/automaton/Transition.java @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.regex.automaton; + +import org.iguana.regex.CharRange; +import org.iguana.regex.EOF; +import org.iguana.utils.collections.hash.MurmurHash3; + +public class Transition implements Comparable { + + private final CharRange range; + + private final State destination; + + public Transition(int start, int end, State destination) { + this(CharRange.in(start, end), destination); + } + + public Transition(int c, State destination) { + this(c, c, destination); + } + + public Transition(CharRange range, State destination) { + if (range.getEnd() < range.getStart()) + throw new IllegalArgumentException("start cannot be less than end."); + + if (destination == null) + throw new IllegalArgumentException("Destination cannot be null."); + + this.range = range; + this.destination = destination; + } + + public static Transition EOFTransition(State destination) { + return new Transition(EOF.VALUE, destination); + } + + public int getStart() { + return range.getStart(); + } + + public int getEnd() { + return range.getEnd(); + } + + public CharRange getRange() { + return range; + } + + public State getDestination() { + return destination; + } + + public boolean isEpsilonTransition() { + return range.getStart() == -1; + } + + public boolean isLoop(State source) { + return source == destination; + } + + public boolean canMove(int c) { + return range.contains(c); + } + + public boolean overlaps(Transition t) { + return range.overlaps(t.range); + } + + @Override + public boolean equals(Object obj) { + + if (this == obj) + return true; + + if (!(obj instanceof Transition)) + return false; + + Transition other = (Transition) obj; + + return isEpsilonTransition() ? other.isEpsilonTransition() && this == other : range.equals(other.range); + } + + @Override + public int hashCode() { + + if (isEpsilonTransition()) + return super.hashCode(); + + return MurmurHash3.f2().apply(range.getStart(), range.getEnd()); + } + + @Override + public String toString() { + if (isEpsilonTransition()) return "-1"; + + return range.toString(); + } + + @Override + public int compareTo(Transition t) { + return range.compareTo(t.range); + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/regex/matcher/DFABackwardsMatcher.java b/benchmarks/src/main/kotlin/org/iguana/regex/matcher/DFABackwardsMatcher.java new file mode 100644 index 000000000..cacf1c895 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/regex/matcher/DFABackwardsMatcher.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.regex.matcher; + +import org.iguana.regex.RegularExpression; +import org.iguana.regex.automaton.AutomatonOperations; +import org.iguana.utils.input.Input; + +public class DFABackwardsMatcher extends DFAMatcher { + + public DFABackwardsMatcher(RegularExpression regex) { + super(AutomatonOperations.reverse(regex.getAutomaton())); + } + + @Override + public int match(Input input, int inputIndex) { + + if (inputIndex == 0) + return -1; + + int length = 0; + int maximumMatched = -1; + int state = start; + + if (finalStates[state]) + maximumMatched = 0; + + for (int i = inputIndex - 1; i >= 0; i--) { + state = table[state].get(input.charAt(i)); + + if (state == ERROR_STATE) + break; + + length++; + + if (finalStates[state]) + maximumMatched = length; + } + + return maximumMatched; + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/regex/matcher/DFAMatcher.java b/benchmarks/src/main/kotlin/org/iguana/regex/matcher/DFAMatcher.java new file mode 100644 index 000000000..35adf8e36 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/regex/matcher/DFAMatcher.java @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.regex.matcher; + +import org.iguana.regex.EOF; +import org.iguana.regex.RegularExpression; +import org.iguana.regex.automaton.Automaton; +import org.iguana.regex.automaton.AutomatonOperations; +import org.iguana.regex.automaton.State; +import org.iguana.regex.automaton.Transition; +import org.iguana.util.Tuple; +import org.iguana.utils.collections.rangemap.IntRangeMap; +import org.iguana.utils.collections.rangemap.RangeMapBuilder; +import org.iguana.utils.input.Input; + +import java.util.HashMap; +import java.util.Map; + +public class DFAMatcher implements Matcher { + + public static final int ERROR_STATE = -2; + + protected IntRangeMap[] table; + + protected final boolean[] finalStates; + + protected final int start; + + protected final Map finalStatesMap; + + private int finalStateId; + + public DFAMatcher(RegularExpression regex) { + this(regex.getAutomaton()); + } + + public DFAMatcher(Automaton automaton) { + automaton = AutomatonOperations.makeDeterministic(automaton); + finalStatesMap = new HashMap<>(); + + finalStates = new boolean[automaton.getStates().length]; + + int size = automaton.getCountStates(); + table = new IntRangeMap[size]; + + for (int i = 0; i < size; i++) { + RangeMapBuilder builder = new RangeMapBuilder<>(); + State state = automaton.getStates()[i]; + for (Transition transition : state.getTransitions()) { + builder.put(transition.getRange(), transition.getDestination().getId()); + } + table[i] = builder.buildIntRangeMap(); + + finalStates[state.getId()] = state.isFinalState(); + if (state.isFinalState()) { + finalStatesMap.put(state.getId(), state); + } + } + + this.start = automaton.getStartState().getId(); + } + + @Override + public int match(Input input, int inputIndex) { + int length = 0; + int maximumMatched = -1; + int state = start; + finalStateId = -1; + + if (finalStates[state]) { + maximumMatched = 0; + finalStateId = state; + } + + for (int i = inputIndex; i < input.length(); i++) { + state = table[state].get(input.charAt(i)); + + if (state == ERROR_STATE) + break; + + length++; + + if (finalStates[state]) { + maximumMatched = length; + finalStateId = state; + } + } + + return maximumMatched; + } + + public RegularExpression getMatchedRegularExpression() { + if (finalStateId == -1) { + throw new IllegalStateException("This method should be called after a successful match."); + } + if (finalStatesMap.get(finalStateId).getRegularExpressions().isEmpty()) { + return EOF.getInstance(); + } + + int min = Integer.MAX_VALUE; + RegularExpression matchedRegularExpression = null; + for (Tuple t : finalStatesMap.get(finalStateId).getRegularExpressions()) { + if (t.getSecond() < min) { + min = t.getSecond(); + matchedRegularExpression = t.getFirst(); + } + } + return matchedRegularExpression; + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/regex/matcher/DFAMatcherFactory.java b/benchmarks/src/main/kotlin/org/iguana/regex/matcher/DFAMatcherFactory.java new file mode 100644 index 000000000..61ea13e6e --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/regex/matcher/DFAMatcherFactory.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.regex.matcher; + +import org.iguana.regex.Char; +import org.iguana.regex.CharRange; +import org.iguana.regex.Epsilon; +import org.iguana.regex.RegularExpression; + +import java.util.HashMap; +import java.util.Map; + +public class DFAMatcherFactory implements MatcherFactory { + + private final Map matcherCache = new HashMap<>(); + private final Map backwardsMatcherCache = new HashMap<>(); + + public Matcher getMatcher(RegularExpression regex) { + if (regex == Epsilon.getInstance()) return epsilonMatcher(); + if (regex instanceof Char) return characterMatcher((Char) regex); + if (regex instanceof CharRange) return characterRangeMatcher((CharRange) regex); + return matcherCache.computeIfAbsent(regex, DFAMatcher::new); + } + + public Matcher getBackwardsMatcher(RegularExpression regex) { + + if (regex instanceof Char) + return characterBackwardsMatcher((Char) regex); + + if (regex instanceof CharRange) + return characterRangeBackwardsMatcher((CharRange) regex); + + return backwardsMatcherCache.computeIfAbsent(regex, DFABackwardsMatcher::new); + } + + public static Matcher characterMatcher(Char c) { + return (input, i) -> input.charAt(i) == c.getValue() ? 1 : -1; + } + + public static Matcher characterBackwardsMatcher(Char c) { + return (input, i) -> i == 0 ? -1 : (input.charAt(i - 1) == c.getValue() ? 1 : -1); + } + + public static Matcher characterRangeMatcher(CharRange range) { + return (input, i) -> input.charAt(i) >= range.getStart() && input.charAt(i) <= range.getEnd() ? 1 : -1; + } + + public static Matcher characterRangeBackwardsMatcher(CharRange range) { + return (input, i) -> i == 0 ? -1 + : (input.charAt(i - 1) >= range.getStart() && input.charAt(i - 1) <= range.getEnd() ? 1 : -1); + } + + public static Matcher epsilonMatcher() { + return (input, i) -> 0; + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/regex/matcher/Main.java b/benchmarks/src/main/kotlin/org/iguana/regex/matcher/Main.java new file mode 100644 index 000000000..5f4ce872b --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/regex/matcher/Main.java @@ -0,0 +1,28 @@ +package org.iguana.regex.matcher; + +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class Main { + + public static void main(String[] args) { + String tuplePattern = "\\(([^(),]+),([^(),]+)\\)"; + String pattern = tuplePattern + "(\\s*" + tuplePattern + ")*"; + + Matcher matcher = Pattern.compile(pattern).matcher("(1.0, 2.3) (12.3 , 7.3) (8, 9) (10, 11)"); + + List groups = new ArrayList<>(); + if (matcher.matches()) { + for (int i = 0; i <= matcher.groupCount(); i++) { + System.out.println(matcher.group(i)); + String group = matcher.group(i).trim(); + if (!group.startsWith("(")) + groups.add(group); + } + } + + System.out.println(groups); + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/regex/matcher/Matcher.java b/benchmarks/src/main/kotlin/org/iguana/regex/matcher/Matcher.java new file mode 100644 index 000000000..4bbb08cf5 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/regex/matcher/Matcher.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.regex.matcher; + +import org.iguana.utils.input.Input; + +/** + * + * + * @author Ali Afroozeh + * + */ + +@FunctionalInterface +public interface Matcher { + + int match(Input input, int i); + + default boolean match(Input input) { + return match(input, 0) == input.length() - 1; + } + + default boolean matchPrefix(Input input) { + return match(input, 0) > -1; + } + + default boolean match(Input input, int start, int end) { + return match(input, start) == (end - start); + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/regex/matcher/MatcherFactory.java b/benchmarks/src/main/kotlin/org/iguana/regex/matcher/MatcherFactory.java new file mode 100644 index 000000000..627c9abd5 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/regex/matcher/MatcherFactory.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.regex.matcher; + +import org.iguana.regex.RegularExpression; + +/** + * + * @author Al Afroozeh + * + */ +public interface MatcherFactory { + + Matcher getMatcher(RegularExpression regex); + + Matcher getBackwardsMatcher(RegularExpression regex); + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/regex/utils/AutomatonToDot.java b/benchmarks/src/main/kotlin/org/iguana/regex/utils/AutomatonToDot.java new file mode 100644 index 000000000..249afe26c --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/regex/utils/AutomatonToDot.java @@ -0,0 +1,49 @@ +package org.iguana.regex.utils; + +import org.iguana.regex.automaton.Automaton; +import org.iguana.regex.automaton.AutomatonVisitor; +import org.iguana.regex.automaton.State; +import org.iguana.regex.automaton.Transition; +import org.iguana.utils.visualization.DotGraph; + +import java.util.IdentityHashMap; +import java.util.Map; + +import static org.iguana.utils.visualization.DotGraph.newEdge; +import static org.iguana.utils.visualization.DotGraph.newNode; + +public class AutomatonToDot { + + private static Map idsMap = new IdentityHashMap<>(); + + public static DotGraph toDot(Automaton automaton) { + idsMap.clear(); + DotGraph dotGraph = new DotGraph(DotGraph.Direction.LEFT_TO_RIGHT); + AutomatonVisitor.visit(automaton, state -> { + DotGraph.Node node = newNode(getId(state)); + if (state.isFinalState()) { + node.setShape(DotGraph.Shape.DOUBLE_CIRCLE); + } else { + node.setShape(DotGraph.Shape.CIRCLE); + } + + node.setLabel(state.getRegularExpressions().isEmpty() ? "" : state.getRegularExpressions().toString()); + + dotGraph.addNode(node); + + for (Transition transition : state.getTransitions()) { + String label; + if (transition.isEpsilonTransition()) + label = "ε"; + else + label = transition.getRange().toString(); + dotGraph.addEdge(newEdge(getId(state), getId(transition.getDestination()), label)); + } + }); + return dotGraph; + } + + private static Integer getId(State state) { + return idsMap.computeIfAbsent(state, k -> idsMap.size() + 1); + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/regex/visitor/GatherReferencesVisitor.java b/benchmarks/src/main/kotlin/org/iguana/regex/visitor/GatherReferencesVisitor.java new file mode 100644 index 000000000..9c432982a --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/regex/visitor/GatherReferencesVisitor.java @@ -0,0 +1,81 @@ +package org.iguana.regex.visitor; + +import org.iguana.regex.Alt; +import org.iguana.regex.Char; +import org.iguana.regex.CharRange; +import org.iguana.regex.EOF; +import org.iguana.regex.Epsilon; +import org.iguana.regex.Opt; +import org.iguana.regex.Plus; +import org.iguana.regex.Reference; +import org.iguana.regex.RegularExpression; +import org.iguana.regex.Seq; +import org.iguana.regex.Star; + +import java.util.ArrayList; +import java.util.List; + +public class GatherReferencesVisitor implements RegularExpressionVisitor { + + private final List references = new ArrayList<>(); + + public List getReferences() { + return references; + } + + @Override + public Void visit(Char c) { + return null; + } + + @Override + public Void visit(CharRange r) { + return null; + } + + @Override + public Void visit(EOF eof) { + return null; + } + + @Override + public Void visit(Epsilon e) { + return null; + } + + @Override + public Void visit(Star s) { + s.getSymbol().accept(this); + return null; + } + + @Override + public Void visit(Plus p) { + p.getSymbol().accept(this); + return null; + } + + @Override + public Void visit(Opt o) { + o.getSymbol().accept(this); + return null; + } + + @Override + public Void visit(Alt alt) { + alt.getSymbols().forEach(symbol -> symbol.accept(this)); + return null; + } + + @Override + public Void visit(Seq seq) { + seq.getSymbols().forEach(symbol -> symbol.accept(this)); + return null; + } + + @Override + public Void visit(Reference ref) { + references.add(ref.getName()); + return null; + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/regex/visitor/InlineReferencesVisitor.java b/benchmarks/src/main/kotlin/org/iguana/regex/visitor/InlineReferencesVisitor.java new file mode 100644 index 000000000..6fdf9ee31 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/regex/visitor/InlineReferencesVisitor.java @@ -0,0 +1,79 @@ +package org.iguana.regex.visitor; + +import org.iguana.regex.EOF; +import org.iguana.regex.RegularExpression; + +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +public class InlineReferencesVisitor implements RegularExpressionVisitor { + + private final Map definitions; + + public InlineReferencesVisitor(Map definitions) { + this.definitions = definitions; + } + + @Override + public RegularExpression visit(org.iguana.regex.Char c) { + return c; + } + + @Override + public RegularExpression visit(org.iguana.regex.CharRange r) { + return r; + } + + @Override + public RegularExpression visit(EOF eof) { + return eof; + } + + @Override + public RegularExpression visit(org.iguana.regex.Epsilon e) { + return e; + } + + @Override + public RegularExpression visit(org.iguana.regex.Star s) { + RegularExpression newSymbol = s.getSymbol().accept(this); + return s.copy().setSymbol(newSymbol).build(); + } + + @Override + public RegularExpression visit(org.iguana.regex.Plus p) { + RegularExpression newSymbol = p.getSymbol().accept(this); + return p.copy().setSymbol(newSymbol).build(); + } + + @Override + public RegularExpression visit(org.iguana.regex.Opt o) { + RegularExpression newSymbol = o.getSymbol().accept(this); + return o.copy().setSymbol(newSymbol).build(); + } + + @Override + public RegularExpression visit(org.iguana.regex.Alt alt) { + List newSymbols = alt.getSymbols().stream().map(symbol -> symbol.accept(this)) + .collect(Collectors.toList()); + return alt.copy().setSymbols((List) newSymbols).build(); + } + + @Override + public RegularExpression visit(org.iguana.regex.Seq seq) { + List newSymbols = seq.getSymbols().stream().map(symbol -> symbol.accept(this)) + .collect(Collectors.toList()); + return seq.copy().setSymbols((List) newSymbols).build(); + } + + @Override + public RegularExpression visit(org.iguana.regex.Reference ref) { + RegularExpression regularExpression = definitions.get(ref.getName()); + if (regularExpression != null) { + return regularExpression; + } else { + return ref; + } + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/regex/visitor/RegularExpressionVisitor.java b/benchmarks/src/main/kotlin/org/iguana/regex/visitor/RegularExpressionVisitor.java new file mode 100644 index 000000000..7e9a26689 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/regex/visitor/RegularExpressionVisitor.java @@ -0,0 +1,37 @@ +package org.iguana.regex.visitor; + +import org.iguana.regex.Alt; +import org.iguana.regex.Char; +import org.iguana.regex.CharRange; +import org.iguana.regex.EOF; +import org.iguana.regex.Epsilon; +import org.iguana.regex.Opt; +import org.iguana.regex.Plus; +import org.iguana.regex.Reference; +import org.iguana.regex.RegularExpression; +import org.iguana.regex.Seq; +import org.iguana.regex.Star; + +public interface RegularExpressionVisitor { + + T visit(Char c); + + T visit(CharRange r); + + T visit(EOF eof); + + T visit(Epsilon e); + + T visit(Star s); + + T visit(Plus p); + + T visit(Opt o); + + T visit(Alt alt); + + T visit(Seq seq); + + T visit(Reference ref); + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/regex/visitor/ToAutomatonRegexVisitor.java b/benchmarks/src/main/kotlin/org/iguana/regex/visitor/ToAutomatonRegexVisitor.java new file mode 100644 index 000000000..d0af5861e --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/regex/visitor/ToAutomatonRegexVisitor.java @@ -0,0 +1,181 @@ +package org.iguana.regex.visitor; + +import org.iguana.regex.Alt; +import org.iguana.regex.Char; +import org.iguana.regex.CharRange; +import org.iguana.regex.EOF; +import org.iguana.regex.Epsilon; +import org.iguana.regex.Opt; +import org.iguana.regex.Plus; +import org.iguana.regex.RegularExpression; +import org.iguana.regex.Seq; +import org.iguana.regex.Star; +import org.iguana.regex.automaton.Automaton; +import org.iguana.regex.automaton.AutomatonBuilder; +import org.iguana.regex.automaton.State; +import org.iguana.regex.automaton.StateType; +import org.iguana.regex.automaton.Transition; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.Function; + +public class ToAutomatonRegexVisitor implements RegularExpressionVisitor { + + // TODO: consider making this cache global, across the regular expressions for the whole grammar. + private final Map cache = new HashMap<>(); + + // Since Java 9, recursive calls to computeIfAbsent throw concurrent modification exception. + // This memoize function is a way to provide the same concise interface but avoid the exceptions. + private Automaton memoize(RegularExpression regex, Function f) { + Automaton automaton = cache.get(regex); + if (automaton != null) return automaton; + automaton = f.apply(regex); + cache.put(regex, automaton); + return automaton; + } + + @Override + public Automaton visit(Char c) { + return memoize(c, regex -> { + State startState = new State(); + State finalState = new State(StateType.FINAL); + startState.addTransition(new Transition(c.getValue(), finalState)); + return Automaton.builder(startState).build(); + }); + } + + @Override + public Automaton visit(CharRange r) { + return memoize(r, regex -> { + State startState = new State(); + State finalState = new State(StateType.FINAL); + startState.addTransition(new Transition(r.getStart(), r.getEnd(), finalState)); + return Automaton.builder(startState).build(); + }); + } + + @Override + public Automaton visit(EOF eof) { + return memoize(eof, regex -> { + State startState = new State(); + State finalState = new State(StateType.FINAL); + startState.addTransition(new Transition(EOF.VALUE, finalState)); + return Automaton.builder(startState).build(); + }); + } + + @Override + public Automaton visit(Epsilon e) { + return memoize(e, regex -> { + State state = new State(StateType.FINAL); + return Automaton.builder(state).build(); + }); + } + + @Override + public Automaton visit(Star star) { + //TODO: add separators to the DFA + return memoize(star, regex -> { + State startState = new State(); + State finalState = new State(StateType.FINAL); + + Automaton starAutomaton = star.getSymbol().accept(this).copy(); + + startState.addEpsilonTransition(starAutomaton.getStartState()); + + Set finalStates = starAutomaton.getFinalStates(); + + for (State s : finalStates) { + s.setStateType(StateType.NORMAL); + s.addEpsilonTransition(finalState); + s.addEpsilonTransition(starAutomaton.getStartState()); + } + + startState.addEpsilonTransition(finalState); + + return Automaton.builder(startState).build(); + }); + } + + @Override + public Automaton visit(Plus p) { + return memoize(p, regex -> Seq.from(p.getSymbol(), Star.from(p.getSymbol())).accept(this)); + } + + @Override + public Automaton visit(Opt opt) { + return memoize(opt, regex -> { + Automaton automaton = opt.getSymbol().accept(this).copy(); + + Set finalStates = automaton.getFinalStates(); + for (State finalState : finalStates) { + automaton.getStartState().addEpsilonTransition(finalState); + } + return automaton; + }); + } + + @Override + public Automaton visit(Alt alt) { + return memoize(alt, regex -> { + List symbols = alt.getSymbols(); + + if (symbols.size() == 1) + return symbols.get(0).accept(this); + + List automatons = new ArrayList<>(); + + for (RegularExpression e : symbols) { + automatons.add(e.accept(this).copy()); + } + + State startState = new State(); + + for (Automaton a : automatons) { + startState.addEpsilonTransition(a.getStartState()); + } + + return new AutomatonBuilder(startState).build(); + }); + } + + @Override + public Automaton visit(Seq seq) { + return memoize(seq, regex -> { + List automatons = new ArrayList<>(); + + for (E symbol : seq.getSymbols()) { + automatons.add(symbol.accept(this).copy()); + } + + Automaton current = automatons.get(0); + State startState = current.getStartState(); + + for (int i = 1; i < automatons.size(); i++) { + Automaton next = automatons.get(i); + + for (State s : current.getFinalStates()) { + s.setStateType(StateType.NORMAL); + // Merge the end state with the start state of the next automaton + for (Transition t : next.getStartState().getTransitions()) { + s.addTransition(new Transition(t.getRange(), t.getDestination())); + } + } + + current = next; + } + + return Automaton.builder(startState).build(); + }); + } + + @Override + public Automaton visit(org.iguana.regex.Reference ref) { + throw new RuntimeException("References should be resolved first"); + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/result/ParserResultOps.java b/benchmarks/src/main/kotlin/org/iguana/result/ParserResultOps.java new file mode 100644 index 000000000..009a8f02b --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/result/ParserResultOps.java @@ -0,0 +1,182 @@ +package org.iguana.result; + +import org.iguana.grammar.slot.BodyGrammarSlot; +import org.iguana.grammar.slot.EndGrammarSlot; +import org.iguana.grammar.slot.GrammarSlot; +import org.iguana.grammar.slot.TerminalGrammarSlot; +import org.iguana.grammar.slot.TerminalNodeType; +import org.iguana.grammar.symbol.Terminal; +import org.iguana.sppf.DefaultTerminalNode; +import org.iguana.sppf.EmptyTerminalNode; +import org.iguana.sppf.EpsilonTerminalNode; +import org.iguana.sppf.ErrorNode; +import org.iguana.sppf.IntermediateNode; +import org.iguana.sppf.KeywordTerminalNode; +import org.iguana.sppf.NonPackedNode; +import org.iguana.sppf.NonterminalNode; +import org.iguana.sppf.NonterminalNodeWithValue; +import org.iguana.sppf.PackedNode; +import org.iguana.sppf.TerminalNode; +import org.iguana.traversal.SPPFVisitor; +import org.iguana.util.ParserLogger; + +import java.util.ArrayList; +import java.util.IdentityHashMap; +import java.util.List; +import java.util.Map; + +public class ParserResultOps implements ResultOps { + + private static final NonPackedNode dummyNode = new NonPackedNode() { + @Override + public PackedNode getChildAt(int index) { + throw new UnsupportedOperationException(); + } + + @Override + public int childrenCount() { + throw new UnsupportedOperationException(); + } + + @Override + public GrammarSlot getGrammarSlot() { + throw new UnsupportedOperationException(); + } + + @Override + public int getLeftExtent() { + throw new UnsupportedOperationException(); + } + + @Override + public int getRightExtent() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isDummy() { + return true; + } + + @Override + public R accept(SPPFVisitor visitor) { + throw new UnsupportedOperationException(); + } + + @Override + public void setAmbiguous(boolean ambiguous) { + } + + @Override + public boolean isAmbiguous() { + return false; + } + + @Override + public PackedNode getFirstPackedNode() { + throw new UnsupportedOperationException(); + } + + @Override + public String toString() { + return "$"; + } + }; + + private ParserLogger logger = ParserLogger.getInstance(); + + private Map> packedNodesMap = new IdentityHashMap<>(); + + @Override + public NonPackedNode dummy() { + return dummyNode; + } + + @Override + public TerminalNode base(TerminalGrammarSlot slot, int start, int end) { + TerminalNode node; + if (start == end) { + node = new EmptyTerminalNode(slot, start); + } else if (slot.getTerminal().getNodeType() == TerminalNodeType.Literal) { + node = new KeywordTerminalNode(slot, start); + } else if (slot.getTerminal() == Terminal.epsilon()) + return new EpsilonTerminalNode(start); + else { + node = new DefaultTerminalNode(slot, start, end); + } + logger.terminalNodeAdded(node); + return node; + } + + @Override + public NonPackedNode error(BodyGrammarSlot slot, int start, int end) { + return new ErrorNode(slot, start, end); + } + + @Override + public NonPackedNode merge( + NonPackedNode current, + NonPackedNode result1, + NonPackedNode result2, + BodyGrammarSlot slot) { + if (result1 == dummyNode) + return result2; + + if (current == null) { + current = new IntermediateNode(slot, result1, result2); + logger.intermediateNodeAdded((IntermediateNode) current); + } else { + List packedNodes = packedNodesMap.computeIfAbsent(current, key -> new ArrayList<>()); + + if (!current.isAmbiguous()) { + PackedNode firstPackedNode = current.getFirstPackedNode(); + logger.packedNodeAdded(firstPackedNode); + packedNodes.add(firstPackedNode); + + current.setAmbiguous(true); + logger.ambiguousNodeAdded(current); + } + + PackedNode packedNode = new PackedNode(slot, result1, result2); + packedNodes.add(packedNode); + logger.packedNodeAdded(packedNode); + } + + return current; + } + + @Override + public NonPackedNode convert(NonPackedNode current, NonPackedNode result, EndGrammarSlot slot, Object value) { + if (current == null) { + if (value == null) + current = new NonterminalNode(slot, result, result.getLeftExtent(), result.getRightExtent()); + else + current = new NonterminalNodeWithValue(slot, result, result.getLeftExtent(), result.getRightExtent(), + value); + + logger.nonterminalNodeAdded((NonterminalNode) current); + } else { + List packedNodes = packedNodesMap.computeIfAbsent(current, key -> new ArrayList<>()); + + if (!current.isAmbiguous()) { + PackedNode firstPackedNode = current.getFirstPackedNode(); + logger.packedNodeAdded(firstPackedNode); + packedNodes.add(firstPackedNode); + + current.setAmbiguous(true); + logger.ambiguousNodeAdded(current); + } + + PackedNode packedNode = new PackedNode(slot, result); + packedNodes.add(packedNode); + logger.packedNodeAdded(packedNode); + } + + return current; + } + + public List getPackedNodes(NonPackedNode node) { + return packedNodesMap.get(node); + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/result/RecognizerResult.java b/benchmarks/src/main/kotlin/org/iguana/result/RecognizerResult.java new file mode 100644 index 000000000..21d911d60 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/result/RecognizerResult.java @@ -0,0 +1,83 @@ +package org.iguana.result; + +public interface RecognizerResult extends Result { + static RecognizerResult of(int start, int end) { + return new SimpleRecognizerResult(start, end); + } + + static RecognizerResult of(int index, Object value) { + return new DataDependentRecognizerResult(index, value); + } +} + +class SimpleRecognizerResult implements RecognizerResult { + + private final int start; + private final int index; + + SimpleRecognizerResult(int start, int end) { + this.start = start; + this.index = end; + } + + @Override + public int getRightExtent() { + return index; + } + + @Override + public int getLeftExtent() { + return start; + } + + @Override + public boolean isDummy() { + return false; + } + + @Override + public Object getValue() { + return null; + } + + @Override + public String toString() { + return index + ""; + } +} + +class DataDependentRecognizerResult implements RecognizerResult { + + private final int index; + private final Object value; + + DataDependentRecognizerResult(int index, Object value) { + this.index = index; + this.value = value; + } + + @Override + public int getRightExtent() { + return index; + } + + @Override + public int getLeftExtent() { + return index; + } + + @Override + public boolean isDummy() { + return false; + } + + @Override + public Object getValue() { + return value; + } + + @Override + public String toString() { + return "(" + "index=" + index + ", value=" + value + ")"; + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/result/RecognizerResultOps.java b/benchmarks/src/main/kotlin/org/iguana/result/RecognizerResultOps.java new file mode 100644 index 000000000..a8105c3a8 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/result/RecognizerResultOps.java @@ -0,0 +1,75 @@ +package org.iguana.result; + +import org.iguana.grammar.slot.BodyGrammarSlot; +import org.iguana.grammar.slot.EndGrammarSlot; +import org.iguana.grammar.slot.TerminalGrammarSlot; + +public class RecognizerResultOps implements ResultOps { + + private static final RecognizerResult dummy = new RecognizerResult() { + @Override + public int getRightExtent() { + return -1; + } + + @Override + public int getLeftExtent() { + return -1; + } + + @Override + public boolean isDummy() { + return true; + } + + @Override + public Object getValue() { + return null; + } + + @Override + public String toString() { + return "$"; + } + }; + + @Override + public RecognizerResult dummy() { + return dummy; + } + + @Override + public RecognizerResult base(TerminalGrammarSlot slot, int start, int end) { + return RecognizerResult.of(start, end); + } + + @Override + public RecognizerResult error(BodyGrammarSlot slot, int start, int end) { + return RecognizerResult.of(start, end); + } + + @Override + public RecognizerResult merge( + RecognizerResult current, + RecognizerResult result1, + RecognizerResult result2, + BodyGrammarSlot slot) { + return result2; + } + + @Override + public RecognizerResult convert( + RecognizerResult current, + RecognizerResult result, + EndGrammarSlot slot, + Object value) { + if (current == null) { + if (value == null) { + return result; + } + return RecognizerResult.of(result.getRightExtent(), value); + } + return result; + } +} + diff --git a/benchmarks/src/main/kotlin/org/iguana/result/Result.java b/benchmarks/src/main/kotlin/org/iguana/result/Result.java new file mode 100644 index 000000000..d211b5cdd --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/result/Result.java @@ -0,0 +1,12 @@ +package org.iguana.result; + +public interface Result { + + int getRightExtent(); + + int getLeftExtent(); + + boolean isDummy(); + + Object getValue(); +} diff --git a/benchmarks/src/main/kotlin/org/iguana/result/ResultOps.java b/benchmarks/src/main/kotlin/org/iguana/result/ResultOps.java new file mode 100644 index 000000000..e46061547 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/result/ResultOps.java @@ -0,0 +1,18 @@ +package org.iguana.result; + +import org.iguana.grammar.slot.BodyGrammarSlot; +import org.iguana.grammar.slot.EndGrammarSlot; +import org.iguana.grammar.slot.TerminalGrammarSlot; + +public interface ResultOps { + + T dummy(); + + T base(TerminalGrammarSlot slot, int start, int end); + + T error(BodyGrammarSlot slot, int start, int end); + + T merge(T current, T result1, T result2, BodyGrammarSlot slot); + + T convert(T current, T result, EndGrammarSlot slot, Object value); +} diff --git a/benchmarks/src/main/kotlin/org/iguana/sppf/DefaultTerminalNode.java b/benchmarks/src/main/kotlin/org/iguana/sppf/DefaultTerminalNode.java new file mode 100644 index 000000000..6c192665a --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/sppf/DefaultTerminalNode.java @@ -0,0 +1,25 @@ +package org.iguana.sppf; + +import org.iguana.grammar.slot.TerminalGrammarSlot; + +public class DefaultTerminalNode extends TerminalNode { + + private final int rightExtent; + private final TerminalGrammarSlot slot; + + public DefaultTerminalNode(TerminalGrammarSlot slot, int leftExtent, int rightExtent) { + super(leftExtent); + this.slot = slot; + this.rightExtent = rightExtent; + } + + @Override + public int getRightExtent() { + return rightExtent; + } + + @Override + public TerminalGrammarSlot getGrammarSlot() { + return slot; + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/sppf/EmptyTerminalNode.java b/benchmarks/src/main/kotlin/org/iguana/sppf/EmptyTerminalNode.java new file mode 100644 index 000000000..596840f1e --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/sppf/EmptyTerminalNode.java @@ -0,0 +1,23 @@ +package org.iguana.sppf; + +import org.iguana.grammar.slot.TerminalGrammarSlot; + +public class EmptyTerminalNode extends TerminalNode { + + private final TerminalGrammarSlot slot; + + public EmptyTerminalNode(TerminalGrammarSlot slot, int leftExtent) { + super(leftExtent); + this.slot = slot; + } + + @Override + public int getRightExtent() { + return getLeftExtent(); + } + + @Override + public TerminalGrammarSlot getGrammarSlot() { + return slot; + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/sppf/EpsilonTerminalNode.java b/benchmarks/src/main/kotlin/org/iguana/sppf/EpsilonTerminalNode.java new file mode 100644 index 000000000..5af190099 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/sppf/EpsilonTerminalNode.java @@ -0,0 +1,21 @@ +package org.iguana.sppf; + +import org.iguana.grammar.GrammarGraph; +import org.iguana.grammar.slot.TerminalGrammarSlot; + +public class EpsilonTerminalNode extends TerminalNode { + + public EpsilonTerminalNode(int leftExtent) { + super(leftExtent); + } + + @Override + public TerminalGrammarSlot getGrammarSlot() { + return GrammarGraph.epsilonSlot; + } + + @Override + public int getRightExtent() { + return getLeftExtent(); + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/sppf/ErrorNode.java b/benchmarks/src/main/kotlin/org/iguana/sppf/ErrorNode.java new file mode 100644 index 000000000..22cc9d9e8 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/sppf/ErrorNode.java @@ -0,0 +1,63 @@ +package org.iguana.sppf; + +import org.iguana.grammar.slot.BodyGrammarSlot; +import org.iguana.grammar.slot.GrammarSlot; +import org.iguana.traversal.SPPFVisitor; + +public class ErrorNode extends NonPackedNode { + + private final BodyGrammarSlot slot; + private final int leftExtent; + private final int rightExtent; + + public ErrorNode(BodyGrammarSlot slot, int leftExtent, int rightExtent) { + this.slot = slot; + this.leftExtent = leftExtent; + this.rightExtent = rightExtent; + } + + @Override + public int getRightExtent() { + return rightExtent; + } + + @Override + public SPPFNode getChildAt(int index) { + throw new UnsupportedOperationException(); + } + + @Override + public int childrenCount() { + return 0; + } + + @Override + public GrammarSlot getGrammarSlot() { + return slot; + } + + @Override + public int getLeftExtent() { + return leftExtent; + } + + @Override + public R accept(SPPFVisitor visitor) { + return visitor.visit(this); + } + + @Override + public void setAmbiguous(boolean ambiguous) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isAmbiguous() { + throw new UnsupportedOperationException(); + } + + @Override + public PackedNode getFirstPackedNode() { + throw new UnsupportedOperationException(); + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/sppf/IntermediateNode.java b/benchmarks/src/main/kotlin/org/iguana/sppf/IntermediateNode.java new file mode 100644 index 000000000..3f63761de --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/sppf/IntermediateNode.java @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.sppf; + +import org.iguana.grammar.slot.BodyGrammarSlot; +import org.iguana.traversal.SPPFVisitor; + +public class IntermediateNode extends NonPackedNode { + + private final NonPackedNode leftChild; + + private final NonPackedNode rightChild; + + private final BodyGrammarSlot slot; + + private boolean ambiguous; + + public IntermediateNode(BodyGrammarSlot slot, NonPackedNode leftChild, NonPackedNode rightChild) { + this.slot = slot; + this.leftChild = leftChild; + this.rightChild = rightChild; + } + + @Override + public R accept(SPPFVisitor visitor) { + return (R) visitor.visit(this); + } + + @Override + public SPPFNode getChildAt(int index) { + if (index == 0) { + return leftChild; + } + if (index == 1) { + return rightChild; + } + throw new ArrayIndexOutOfBoundsException(); + } + + @Override + public int childrenCount() { + return 2; + } + + @Override + public BodyGrammarSlot getGrammarSlot() { + return slot; + } + + @Override + public int getLeftExtent() { + return leftChild.getLeftExtent(); + } + + @Override + public int getRightExtent() { + return rightChild.getRightExtent(); + } + + @Override + public void setAmbiguous(boolean ambiguous) { + this.ambiguous = ambiguous; + } + + @Override + public boolean isAmbiguous() { + return ambiguous; + } + + @Override + public PackedNode getFirstPackedNode() { + return new PackedNode(slot, leftChild, rightChild); + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/sppf/KeywordTerminalNode.java b/benchmarks/src/main/kotlin/org/iguana/sppf/KeywordTerminalNode.java new file mode 100644 index 000000000..48a3394d8 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/sppf/KeywordTerminalNode.java @@ -0,0 +1,25 @@ +package org.iguana.sppf; + +import org.iguana.grammar.slot.TerminalGrammarSlot; + +public class KeywordTerminalNode extends TerminalNode { + + private final int rightExtent; + private final TerminalGrammarSlot slot; + + public KeywordTerminalNode(TerminalGrammarSlot slot, int leftExtent) { + super(leftExtent); + this.slot = slot; + this.rightExtent = leftExtent + slot.getTerminal().getRegularExpression().length(); + } + + @Override + public int getRightExtent() { + return rightExtent; + } + + @Override + public TerminalGrammarSlot getGrammarSlot() { + return slot; + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/sppf/NonPackedNode.java b/benchmarks/src/main/kotlin/org/iguana/sppf/NonPackedNode.java new file mode 100644 index 000000000..5090ea5c1 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/sppf/NonPackedNode.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.sppf; + +public abstract class NonPackedNode implements SPPFNode { + + public abstract void setAmbiguous(boolean ambiguous); + + public abstract boolean isAmbiguous(); + + public abstract PackedNode getFirstPackedNode(); + + @Override + public String toString() { + return String.format("(%s, %d, %d)", getGrammarSlot(), getLeftExtent(), getRightExtent()); + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/sppf/NonterminalNode.java b/benchmarks/src/main/kotlin/org/iguana/sppf/NonterminalNode.java new file mode 100644 index 000000000..ebf4847c5 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/sppf/NonterminalNode.java @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.sppf; + +import org.iguana.grammar.runtime.RuntimeRule; +import org.iguana.grammar.slot.EndGrammarSlot; +import org.iguana.grammar.slot.NonterminalGrammarSlot; +import org.iguana.traversal.SPPFVisitor; + +public class NonterminalNode extends NonPackedNode { + + private final EndGrammarSlot slot; + + private final NonPackedNode child; + + private final int leftExtent; + + private final int rightExtent; + + private boolean ambiguous; + + public NonterminalNode(EndGrammarSlot slot, NonPackedNode child, int leftExtent, int rightExtent) { + this.slot = slot; + this.child = child; + this.leftExtent = leftExtent; + this.rightExtent = rightExtent; + } + + @Override + public SPPFNode getChildAt(int index) { + if (index == 0) { + return child; + } + throw new IndexOutOfBoundsException(); + } + + @Override + public int childrenCount() { + return 1; + } + + @Override + public NonterminalGrammarSlot getGrammarSlot() { + return slot.getNonterminal(); + } + + public EndGrammarSlot getEndGrammarSlot() { + return slot; + } + + public RuntimeRule getRule() { + return slot.getRule(); + } + + @Override + public R accept(SPPFVisitor visitor) { + return visitor.visit(this); + } + + @Override + public int getLeftExtent() { + return leftExtent; + } + + @Override + public void setAmbiguous(boolean ambiguous) { + this.ambiguous = ambiguous; + } + + @Override + public boolean isAmbiguous() { + return ambiguous; + } + + @Override + public String toString() { + return String.format("(%s, %d, %d)", slot, getLeftExtent(), getRightExtent()); + } + + @Override + public int getRightExtent() { + return rightExtent; + } + + @Override + public PackedNode getFirstPackedNode() { + return new PackedNode(slot, child); + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/sppf/NonterminalNodeWithValue.java b/benchmarks/src/main/kotlin/org/iguana/sppf/NonterminalNodeWithValue.java new file mode 100644 index 000000000..24f05f4d6 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/sppf/NonterminalNodeWithValue.java @@ -0,0 +1,18 @@ +package org.iguana.sppf; + +import org.iguana.grammar.slot.EndGrammarSlot; + +public class NonterminalNodeWithValue extends NonterminalNode { + + private final Object value; + + public NonterminalNodeWithValue( + EndGrammarSlot slot, NonPackedNode child, int leftExtent, int rightExtent, Object value) { + super(slot, child, leftExtent, rightExtent); + this.value = value; + } + + public Object getValue() { + return value; + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/sppf/PackedNode.java b/benchmarks/src/main/kotlin/org/iguana/sppf/PackedNode.java new file mode 100644 index 000000000..3bcadf881 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/sppf/PackedNode.java @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.sppf; + +import org.iguana.grammar.slot.BodyGrammarSlot; +import org.iguana.traversal.SPPFVisitor; + +public class PackedNode implements SPPFNode { + + private final BodyGrammarSlot slot; + + private NonPackedNode leftChild; + + private NonPackedNode rightChild; + + public PackedNode(BodyGrammarSlot slot) { + this.slot = slot; + } + + public PackedNode(BodyGrammarSlot slot, NonPackedNode leftChild) { + this(slot, leftChild, null); + } + + public PackedNode(BodyGrammarSlot slot, NonPackedNode leftChild, NonPackedNode rightChild) { + this.slot = slot; + this.leftChild = leftChild; + this.rightChild = rightChild; + } + + public void setLeftChild(NonPackedNode leftChild) { + this.leftChild = leftChild; + } + + public void setRightChild(NonPackedNode rightChild) { + this.rightChild = rightChild; + } + + private int getPivot() { + return leftChild.getRightExtent(); + } + + @Override + public BodyGrammarSlot getGrammarSlot() { + return slot; + } + + @Override + public int getLeftExtent() { + return leftChild.getLeftExtent(); + } + + @Override + public int getRightExtent() { + return (rightChild != null) ? rightChild.getRightExtent() : leftChild.getRightExtent(); + } + + @Override + public String toString() { + return String.format("(%s, %d)", slot, getPivot()); + } + + @Override + public R accept(SPPFVisitor visitor) { + return (R) visitor.visit(this); + } + + public NonPackedNode getLeftChild() { + return leftChild; + } + + public NonPackedNode getRightChild() { + return rightChild; + } + + @Override + public NonPackedNode getChildAt(int index) { + if (index == 0) + return leftChild; + else if (index == 1) + return rightChild; + else + throw new RuntimeException("index should be only 0 or 1."); + } + + @Override + public int childrenCount() { + return (rightChild == null) ? 1 : 2; + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/sppf/SPPFNode.java b/benchmarks/src/main/kotlin/org/iguana/sppf/SPPFNode.java new file mode 100644 index 000000000..f854c3ce9 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/sppf/SPPFNode.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.sppf; + +import org.iguana.grammar.slot.GrammarSlot; +import org.iguana.result.Result; +import org.iguana.traversal.SPPFVisitor; + +public interface SPPFNode extends Result { + + SPPFNode getChildAt(int index); + + int childrenCount(); + + GrammarSlot getGrammarSlot(); + + int getLeftExtent(); + + R accept(SPPFVisitor visitor); + + default Object getValue() { + return null; + } + + default boolean isDummy() { return false; } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/sppf/TerminalNode.java b/benchmarks/src/main/kotlin/org/iguana/sppf/TerminalNode.java new file mode 100644 index 000000000..bbbb82d2a --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/sppf/TerminalNode.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.sppf; + +import org.iguana.grammar.slot.TerminalGrammarSlot; +import org.iguana.traversal.SPPFVisitor; + +public abstract class TerminalNode extends NonPackedNode { + + private final int leftExtent; + + public TerminalNode(int leftExtent) { + this.leftExtent = leftExtent; + } + + @Override + public R accept(SPPFVisitor visitor) { + return visitor.visit(this); + } + + @Override + public int childrenCount() { + return 0; + } + + @Override + public abstract TerminalGrammarSlot getGrammarSlot(); + + @Override + public PackedNode getChildAt(int index) { + throw new UnsupportedOperationException(); + } + + @Override + public int getLeftExtent() { + return leftExtent; + } + + @Override + public void setAmbiguous(boolean ambiguous) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isAmbiguous() { + return false; + } + + @Override + public PackedNode getFirstPackedNode() { + throw new UnsupportedOperationException(); + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/traversal/AbstractSymbolVisitor.java b/benchmarks/src/main/kotlin/org/iguana/traversal/AbstractSymbolVisitor.java new file mode 100644 index 000000000..e9bb18a97 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/traversal/AbstractSymbolVisitor.java @@ -0,0 +1,101 @@ +package org.iguana.traversal; + +import org.iguana.grammar.symbol.Align; +import org.iguana.grammar.symbol.Alt; +import org.iguana.grammar.symbol.Block; +import org.iguana.grammar.symbol.Code; +import org.iguana.grammar.symbol.Conditional; +import org.iguana.grammar.symbol.Group; +import org.iguana.grammar.symbol.IfThen; +import org.iguana.grammar.symbol.IfThenElse; +import org.iguana.grammar.symbol.Ignore; +import org.iguana.grammar.symbol.Offside; +import org.iguana.grammar.symbol.Opt; +import org.iguana.grammar.symbol.Plus; +import org.iguana.grammar.symbol.Star; +import org.iguana.grammar.symbol.Symbol; +import org.iguana.grammar.symbol.While; + +import java.util.List; +import java.util.stream.Collectors; + +public abstract class AbstractSymbolVisitor implements ISymbolVisitor { + + public abstract T combine(Symbol symbol, List values); + + public T visitChildren(Symbol symbol) { + List result = symbol.getChildren().stream().map(s -> s.accept(this)).collect(Collectors.toList()); + return combine(symbol, result); + } + + @Override + public T visit(Align align) { + return visitChildren(align); + } + + @Override + public T visit(Block block) { + return visitChildren(block); + } + + @Override + public T visit(Code code) { + return visitChildren(code); + } + + @Override + public T visit(Conditional conditional) { + return visitChildren(conditional); + } + + @Override + public T visit(IfThen ifThen) { + return visitChildren(ifThen); + } + + @Override + public T visit(IfThenElse ifThenElse) { + return visitChildren(ifThenElse); + } + + @Override + public T visit(Ignore ignore) { + return visitChildren(ignore); + } + + @Override + public T visit(Offside offside) { + return visitChildren(offside); + } + + @Override + public T visit(While whileSymbol) { + return visitChildren(whileSymbol); + } + + @Override + public T visit(Alt alt) { + return visitChildren(alt); + } + + @Override + public T visit(Opt opt) { + return visitChildren(opt); + } + + @Override + public T visit(Plus plus) { + return visitChildren(plus); + } + + @Override + public T visit(Group group) { + return visitChildren(group); + } + + @Override + public T visit(Star star) { + return visitChildren(star); + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/traversal/AmbiguousSPPFToParseTreeVisitor.java b/benchmarks/src/main/kotlin/org/iguana/traversal/AmbiguousSPPFToParseTreeVisitor.java new file mode 100644 index 000000000..27bbbc289 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/traversal/AmbiguousSPPFToParseTreeVisitor.java @@ -0,0 +1,213 @@ +package org.iguana.traversal; + +import org.iguana.grammar.slot.NonterminalNodeType; +import org.iguana.grammar.slot.TerminalNodeType; +import org.iguana.grammar.symbol.Nonterminal; +import org.iguana.grammar.symbol.Symbol; +import org.iguana.parsetree.MetaSymbolNode; +import org.iguana.parsetree.ParseTreeBuilder; +import org.iguana.parsetree.VisitResult; +import org.iguana.result.ParserResultOps; +import org.iguana.sppf.ErrorNode; +import org.iguana.sppf.IntermediateNode; +import org.iguana.sppf.NonPackedNode; +import org.iguana.sppf.NonterminalNode; +import org.iguana.sppf.PackedNode; +import org.iguana.sppf.TerminalNode; +import org.iguana.traversal.exception.CyclicGrammarException; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static java.util.Collections.singletonList; +import static org.iguana.parsetree.VisitResult.EBNF; +import static org.iguana.parsetree.VisitResult.ebnf; +import static org.iguana.parsetree.VisitResult.empty; +import static org.iguana.parsetree.VisitResult.list; +import static org.iguana.parsetree.VisitResult.single; + +public class AmbiguousSPPFToParseTreeVisitor implements SPPFVisitor { + + private final ParseTreeBuilder parseTreeBuilder; + private final Set visitedNodes; + private final Map convertedNodes; + private final boolean ignoreLayout; + private final ParserResultOps resultOps; + + private final VisitResult.CreateParseTreeVisitor createNodeVisitor; + + public AmbiguousSPPFToParseTreeVisitor( + ParseTreeBuilder parseTreeBuilder, + boolean ignoreLayout, + ParserResultOps resultOps) { + this.parseTreeBuilder = parseTreeBuilder; + this.ignoreLayout = ignoreLayout; + this.resultOps = resultOps; + this.convertedNodes = new HashMap<>(); + this.visitedNodes = new LinkedHashSet<>(); + this.createNodeVisitor = new VisitResult.CreateParseTreeVisitor<>(parseTreeBuilder); + } + + @Override + public VisitResult visit(TerminalNode node) { + if (ignoreLayout && node.getGrammarSlot().getTerminal().getNodeType() == TerminalNodeType.Layout) { + return empty(); + } + return convertedNodes.computeIfAbsent(node, key -> { + if (node.getLeftExtent() == node.getRightExtent()) return empty(); + Object terminalNode = parseTreeBuilder.terminalNode(node.getGrammarSlot().getTerminal(), + node.getLeftExtent(), node.getRightExtent()); + return single(terminalNode); + } + ); + } + + @Override + public VisitResult visit(org.iguana.sppf.NonterminalNode node) { + if (ignoreLayout && node.getGrammarSlot().getNonterminal().getNodeType() == NonterminalNodeType.Layout) { + return empty(); + } + + VisitResult result = convertedNodes.get(node); + if (result != null) return result; + + // To guard for cyclic SPPFs + if (visitedNodes.contains(node)) { + List cycle = new ArrayList<>(); + boolean seen = false; + for (NonterminalNode n : visitedNodes) { + if (seen) { + cycle.add(n.getGrammarSlot().getNonterminal()); + } else { + if (n == node) { + cycle.add(n.getGrammarSlot().getNonterminal()); + seen = true; + } + } + } + cycle.add(node.getGrammarSlot().getNonterminal()); + throw new CyclicGrammarException(cycle); + } else { + visitedNodes.add(node); + } + + if (node.isAmbiguous()) { + LinkedHashSet children = new LinkedHashSet<>(); + for (PackedNode packedNode : resultOps.getPackedNodes(node)) { + VisitResult visitResult = packedNode.accept(this); + children.addAll(visitResult.accept(createNodeVisitor, packedNode)); + } + result = single(parseTreeBuilder.ambiguityNode(children)); + } else { + PackedNode packedNode = node.getFirstPackedNode(); + switch (node.getGrammarSlot().getNodeType()) { + case Basic: + case Layout: { + List children = packedNode.accept(this).accept(createNodeVisitor, packedNode); + if (children.size() > 1) { + result = single(parseTreeBuilder.ambiguityNode(new LinkedHashSet<>(children))); + } else { + T child = children.get(0); + if (child instanceof MetaSymbolNode) { // Last Plus node propagated up + result = single(parseTreeBuilder.nonterminalNode(packedNode.getGrammarSlot().getRule(), + children, packedNode.getLeftExtent(), packedNode.getRightExtent())); + } else { + result = single(children.get(0)); + } + } + break; + } + + case Plus: { + Symbol symbol = packedNode.getGrammarSlot().getRule().getDefinition(); + VisitResult visitResult = packedNode.accept(this); + result = ebnf(visitResult.getValues(), symbol); + break; + } + + case Star: + case Seq: + case Alt: + case Opt: + case Start: { + Symbol symbol = packedNode.getGrammarSlot().getRule().getDefinition(); + VisitResult visitResult = packedNode.accept(this); + // This case handles X+ nodes under other EBNF nodes (See Test 14) + if (visitResult instanceof VisitResult.List + && visitResult.getValues().size() == 1 + && visitResult.getValues().get(0) instanceof VisitResult.EBNF) { + VisitResult.EBNF ebnfChild = (VisitResult.EBNF) visitResult.getValues().get(0); + T ebnfResult = parseTreeBuilder.metaSymbolNode(ebnfChild.getSymbol(), + (List) ebnfChild.getValues(), node.getLeftExtent(), node.getRightExtent()); + result = single(parseTreeBuilder.metaSymbolNode(symbol, singletonList(ebnfResult), + node.getLeftExtent(), node.getRightExtent())); + } else { + result = single(parseTreeBuilder.metaSymbolNode(symbol, (List) visitResult.getValues(), + node.getLeftExtent(), node.getRightExtent())); + } + + break; + } + } + } + visitedNodes.remove(node); + convertedNodes.put(node, result); + return result; + } + + @Override + public VisitResult visit(IntermediateNode node) { + VisitResult result = convertedNodes.get(node); + if (result != null) return result; + + if (node.isAmbiguous()) { + result = empty(); + for (PackedNode packedNode : resultOps.getPackedNodes(node)) { + result = result.merge(packedNode.accept(this)); + } + } else { + PackedNode packedNode = node.getFirstPackedNode(); + result = packedNode.accept(this); + } + convertedNodes.put(node, result); + return result; + } + + @Override + public VisitResult visit(PackedNode node) { + VisitResult left = node.getLeftChild().accept(this); + VisitResult right; + if (node.getRightChild() != null) + right = node.getRightChild().accept(this); + else + right = empty(); + + // It seems that we can simplify the SPPF to ParseTree creation by checking the packed node's node type + // and may be able to get rid of VisitResult hierarchy + if (node.getGrammarSlot().getRule().getHead().getNodeType() != NonterminalNodeType.Plus + && node.getGrammarSlot().getRule().getHead().getNodeType() != NonterminalNodeType.Star) { + if (left instanceof EBNF) { + List values = new ArrayList<>(); + values.add(left); + if (right instanceof EBNF) { + values.add(right); + } else { + values.addAll(right.getValues()); + } + return list(values); + } + } + + return left.merge(right); + } + + @Override + public VisitResult visit(ErrorNode node) { + return VisitResult.single(parseTreeBuilder.errorNode(node.getLeftExtent(), node.getRightExtent())); + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/traversal/DefaultSPPFToParseTreeVisitor.java b/benchmarks/src/main/kotlin/org/iguana/traversal/DefaultSPPFToParseTreeVisitor.java new file mode 100644 index 000000000..410e53a2b --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/traversal/DefaultSPPFToParseTreeVisitor.java @@ -0,0 +1,287 @@ +package org.iguana.traversal; + +import org.iguana.grammar.runtime.RuntimeRule; +import org.iguana.grammar.slot.BodyGrammarSlot; +import org.iguana.grammar.slot.NonterminalNodeType; +import org.iguana.grammar.slot.TerminalNodeType; +import org.iguana.grammar.symbol.Group; +import org.iguana.grammar.symbol.Plus; +import org.iguana.grammar.symbol.Star; +import org.iguana.grammar.symbol.Start; +import org.iguana.grammar.symbol.Symbol; +import org.iguana.parsetree.ParseTreeBuilder; +import org.iguana.result.ParserResultOps; +import org.iguana.sppf.EmptyTerminalNode; +import org.iguana.sppf.ErrorNode; +import org.iguana.sppf.IntermediateNode; +import org.iguana.sppf.NonPackedNode; +import org.iguana.sppf.NonterminalNode; +import org.iguana.sppf.PackedNode; +import org.iguana.sppf.TerminalNode; +import org.iguana.traversal.exception.AmbiguityException; +import org.iguana.util.visualization.SPPFToDot; +import org.iguana.utils.input.Input; +import org.iguana.utils.visualization.DotGraph; + +import java.util.ArrayList; +import java.util.List; + +import static java.util.Collections.emptyList; + +/** + * Unambiguous Nonterminal nodes have only one child. + * Unambiguous Intermediate nodes two children: + * - The left child is an intermediate node and the right child a nonterminal or terminal node + * - Both left and right children are nonterminal or terminal nodes + */ +public class DefaultSPPFToParseTreeVisitor implements SPPFVisitor { + + private final ParseTreeBuilder parseTreeBuilder; + private final Input input; + private final boolean ignoreLayout; + private final ParserResultOps resultOps; + + public DefaultSPPFToParseTreeVisitor( + ParseTreeBuilder parseTreeBuilder, + Input input, + boolean ignoreLayout, + ParserResultOps resultOps) { + this.parseTreeBuilder = parseTreeBuilder; + this.input = input; + this.ignoreLayout = ignoreLayout; + this.resultOps = resultOps; + } + + @Override + public T visit(TerminalNode node) { + if (ignoreLayout && node.getGrammarSlot().getTerminal().getNodeType() == TerminalNodeType.Layout) { + return null; + } + return parseTreeBuilder.terminalNode(node.getGrammarSlot().getTerminal(), node.getLeftExtent(), + node.getRightExtent()); + } + + @Override + public T visit(NonterminalNode node) { + if (node.isAmbiguous()) { + handleAmbiguousNode(node); + } + + if (ignoreLayout && node.getGrammarSlot().getNonterminal().getNodeType() == NonterminalNodeType.Layout) { + return null; + } + + NonPackedNode firstChild = (NonPackedNode) node.getChildAt(0); + + int leftExtent = node.getLeftExtent(); + int rightExtent = node.getRightExtent(); + + switch (node.getGrammarSlot().getNodeType()) { + case Layout: + case Basic: + return convertBasicAndLayout(node.getEndGrammarSlot(), firstChild, leftExtent, rightExtent); + + case Star: + return convertStar(firstChild, (Star) node.getRule().getDefinition(), leftExtent, rightExtent); + + case Plus: + return convertPlus(firstChild, (Plus) node.getRule().getDefinition(), leftExtent, rightExtent); + + case Seq: + return convertSeq(firstChild, (Group) node.getRule().getDefinition(), leftExtent, rightExtent); + + case Start: + return convertStart(firstChild, (Start) node.getRule().getDefinition(), leftExtent, rightExtent); + + case Alt: + case Opt: + return convertAltOpt(firstChild, node.getRule().getDefinition(), leftExtent, rightExtent); + + default: + throw new RuntimeException("Unknown node type"); + } + } + + @Override + public Object visit(IntermediateNode node) { + if (node.isAmbiguous()) { + throw new AmbiguityException(node, input); + } + + List children = new ArrayList<>(); + + NonPackedNode leftChild = (NonPackedNode) node.getChildAt(0); + NonPackedNode rightChild = (NonPackedNode) node.getChildAt(1); + + T result = rightChild.accept(this); + if (result != null) { + children.add(result); + } + + addChildren(leftChild.accept(this), children); + + return children; + } + + @Override + public Object visit(PackedNode node) { + throw new RuntimeException("Should not visit packed nodes."); + } + + @Override + public T visit(ErrorNode node) { + return parseTreeBuilder.errorNode(node.getLeftExtent(), node.getRightExtent()); + } + + private T convertBasicAndLayout(BodyGrammarSlot slot, NonPackedNode child, int leftExtent, int rightExtent) { + int bodySize = slot.getRule().getBody().size(); + if (ignoreLayout) { // Layout is insert between symbols in the body of a rule + bodySize /= 2 + 1; + } + List children = new ArrayList<>(bodySize); + + addChildren(child.accept(this), children); + + reverse(children); + return parseTreeBuilder.nonterminalNode(slot.getRule(), children, leftExtent, rightExtent); + } + + private static void addChildren(T result, List children) { + if (result instanceof List) { + children.addAll((List) result); + } else { + if (result != null) { + children.add(result); + } + } + } + + private T convertStar(NonPackedNode node, Star symbol, int leftExtent, int rightExtent) { + if (node.isAmbiguous()) { + handleAmbiguousNode(node); + } + List children; + if (node instanceof EmptyTerminalNode) { // Empty star + children = emptyList(); + } else { + Plus plus = (Plus) ((NonterminalNode) node).getRule().getDefinition(); + children = flattenChildrenUnderPlus((NonPackedNode) node.getChildAt(0), plus); + return parseTreeBuilder.metaSymbolNode(symbol, children, leftExtent, rightExtent); + } + return parseTreeBuilder.metaSymbolNode(symbol, children, leftExtent, rightExtent); + } + + private T convertPlus(NonPackedNode leftChild, Plus symbol, int leftExtent, int rightExtent) { + List children = flattenChildrenUnderPlus(leftChild, symbol); + return parseTreeBuilder.metaSymbolNode(symbol, children, leftExtent, rightExtent); + } + + private List flattenChildrenUnderPlus(NonPackedNode leftChild, Plus symbol) { + NonPackedNode node = leftChild; + + List children = new ArrayList<>(); + + while (true) { + if (node instanceof IntermediateNode) { + NonterminalNode nextPlusNode = convertUnderPlus(symbol, (IntermediateNode) node, children); + if (nextPlusNode == null) { + break; + } else { + node = (NonPackedNode) nextPlusNode.getChildAt(0); + } + } else { + T result = node.accept(this); + if (result != null) { + children.add(result); + } + break; + } + } + + reverse(children); + return children; + } + + private T convertSeq(NonPackedNode node, Group symbol, int leftExtent, int rightExtent) { + List children = new ArrayList<>(); + addChildren(node.accept(this), children); + reverse(children); + return parseTreeBuilder.metaSymbolNode(symbol, children, leftExtent, rightExtent); + } + + private T convertStart(NonPackedNode node, Start symbol, int leftExtent, int rightExtent) { + List children = new ArrayList<>( + ignoreLayout ? 3 : 1); // Layout is inserted before and after the start symbol + addChildren(node.accept(this), children); + reverse(children); + return parseTreeBuilder.metaSymbolNode(symbol, children, leftExtent, rightExtent); + } + + private T convertAltOpt(NonPackedNode node, Symbol symbol, int leftExtent, int rightExtent) { + List children; + T result = node.accept(this); + if (result == null) { + children = emptyList(); + } else { + children = new ArrayList<>(1); + children.add(result); + } + return parseTreeBuilder.metaSymbolNode(symbol, children, leftExtent, rightExtent); + } + + private NonterminalNode convertUnderPlus(Plus plus, IntermediateNode node, List children) { + if (node.isAmbiguous()) { + handleAmbiguousNode(node); + } + + NonPackedNode leftChild = (NonPackedNode) node.getChildAt(0); + NonPackedNode rightChild = (NonPackedNode) node.getChildAt(1); + + T result = rightChild.accept(this); + if (result != null) { + children.add(result); + } + + if (leftChild instanceof IntermediateNode) { + return convertUnderPlus(plus, (IntermediateNode) leftChild, children); + } else { + if (leftChild instanceof NonterminalNode) { + RuntimeRule rule = ((NonterminalNode) leftChild).getRule(); + if (rule.getDefinition() != null && plus.getName().equals(rule.getDefinition().getName())) { + return (NonterminalNode) leftChild; + } + } + result = leftChild.accept(this); + if (result != null) { + children.add(result); + } + } + return null; + } + + private void handleAmbiguousNode(NonPackedNode node) { + List packedNodes = resultOps.getPackedNodes(node); + for (int i = 0; i < packedNodes.size(); i++) { + try { + DotGraph dotGraph = SPPFToDot.getDotGraph(packedNodes.get(i), input); + dotGraph.generate("/Users/afroozeh/tree" + i + ".pdf"); + } catch (Exception e) { + e.printStackTrace(); + } + } + + throw new AmbiguityException(node, input); + } + + private void reverse(List list) { + int size = list.size(); + if (size == 0 || size == 1) { + return; + } + for (int i = 0; i < size / 2; i++) { + T tmp = list.get(size - i - 1); + list.set(size - i - 1, list.get(i)); + list.set(i, tmp); + } + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/traversal/IConditionVisitor.java b/benchmarks/src/main/kotlin/org/iguana/traversal/IConditionVisitor.java new file mode 100644 index 000000000..f995f93b6 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/traversal/IConditionVisitor.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.traversal; + +import org.iguana.grammar.condition.DataDependentCondition; +import org.iguana.grammar.condition.PositionalCondition; +import org.iguana.grammar.condition.RegularExpressionCondition; + +public interface IConditionVisitor { + + T visit(DataDependentCondition condition); + + T visit(PositionalCondition condition); + + T visit(RegularExpressionCondition condition); + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/traversal/ISymbolVisitor.java b/benchmarks/src/main/kotlin/org/iguana/traversal/ISymbolVisitor.java new file mode 100644 index 000000000..0513defc9 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/traversal/ISymbolVisitor.java @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.traversal; + +import org.iguana.grammar.symbol.Align; +import org.iguana.grammar.symbol.Alt; +import org.iguana.grammar.symbol.Block; +import org.iguana.grammar.symbol.Code; +import org.iguana.grammar.symbol.CodeHolder; +import org.iguana.grammar.symbol.Conditional; +import org.iguana.grammar.symbol.Error; +import org.iguana.grammar.symbol.Group; +import org.iguana.grammar.symbol.Identifier; +import org.iguana.grammar.symbol.IfThen; +import org.iguana.grammar.symbol.IfThenElse; +import org.iguana.grammar.symbol.Ignore; +import org.iguana.grammar.symbol.Nonterminal; +import org.iguana.grammar.symbol.Offside; +import org.iguana.grammar.symbol.Opt; +import org.iguana.grammar.symbol.Plus; +import org.iguana.grammar.symbol.Return; +import org.iguana.grammar.symbol.Star; +import org.iguana.grammar.symbol.Start; +import org.iguana.grammar.symbol.Terminal; +import org.iguana.grammar.symbol.While; + +public interface ISymbolVisitor { + + T visit(Align align); + + T visit(Block block); + + T visit(Code code); + + default T visit(CodeHolder codeHolder) { return null; } + + T visit(Error error); + + T visit(Conditional conditional); + + T visit(IfThen ifThen); + + T visit(IfThenElse ifThenElse); + + T visit(Ignore ignore); + + T visit(Nonterminal nonterminal); + + T visit(Offside offside); + + T visit(Terminal terminal); + + T visit(While whileSymbol); + + T visit(Return returnSymbol); + + T visit(Alt alt); + + T visit(Opt opt); + + T visit(Plus plus); + + T visit(Group group); + + T visit(Star star); + + T visit(Start start); + + default T visit(Identifier identifier) { + throw new UnsupportedOperationException(); + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/traversal/SPPFVisitor.java b/benchmarks/src/main/kotlin/org/iguana/traversal/SPPFVisitor.java new file mode 100644 index 000000000..3db356062 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/traversal/SPPFVisitor.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.traversal; + +import org.iguana.sppf.ErrorNode; +import org.iguana.sppf.IntermediateNode; +import org.iguana.sppf.NonterminalNode; +import org.iguana.sppf.PackedNode; +import org.iguana.sppf.TerminalNode; + +/** + * Provides a standard interface based on the command pattern for + * executing actions in visitors of an SPPF. + * + * @author Ali Afroozeh + * + * + */ +public interface SPPFVisitor { + + T visit(TerminalNode node); + + T visit(NonterminalNode node); + + Object visit(IntermediateNode node); + + Object visit(PackedNode node); + + T visit(ErrorNode node); + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/traversal/SymbolToSymbolVisitor.java b/benchmarks/src/main/kotlin/org/iguana/traversal/SymbolToSymbolVisitor.java new file mode 100644 index 000000000..bae74f9eb --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/traversal/SymbolToSymbolVisitor.java @@ -0,0 +1,261 @@ +package org.iguana.traversal; + +import org.iguana.grammar.condition.Condition; +import org.iguana.grammar.condition.DataDependentCondition; +import org.iguana.grammar.condition.PositionalCondition; +import org.iguana.grammar.condition.RegularExpressionCondition; +import org.iguana.grammar.symbol.Align; +import org.iguana.grammar.symbol.Alt; +import org.iguana.grammar.symbol.Block; +import org.iguana.grammar.symbol.Code; +import org.iguana.grammar.symbol.CodeHolder; +import org.iguana.grammar.symbol.Conditional; +import org.iguana.grammar.symbol.Error; +import org.iguana.grammar.symbol.Group; +import org.iguana.grammar.symbol.Identifier; +import org.iguana.grammar.symbol.IfThen; +import org.iguana.grammar.symbol.IfThenElse; +import org.iguana.grammar.symbol.Ignore; +import org.iguana.grammar.symbol.Nonterminal; +import org.iguana.grammar.symbol.Offside; +import org.iguana.grammar.symbol.Opt; +import org.iguana.grammar.symbol.Plus; +import org.iguana.grammar.symbol.Return; +import org.iguana.grammar.symbol.Star; +import org.iguana.grammar.symbol.Start; +import org.iguana.grammar.symbol.Symbol; +import org.iguana.grammar.symbol.Terminal; +import org.iguana.grammar.symbol.While; +import org.iguana.regex.Char; +import org.iguana.regex.CharRange; +import org.iguana.regex.EOF; +import org.iguana.regex.Epsilon; +import org.iguana.regex.Reference; +import org.iguana.regex.RegularExpression; +import org.iguana.regex.visitor.RegularExpressionVisitor; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +public interface SymbolToSymbolVisitor + extends ISymbolVisitor, IConditionVisitor, RegularExpressionVisitor { + + @Override + default Symbol visit(Align symbol) { + return visitSymbol(symbol); + } + + @Override + default Symbol visit(Block symbol) { + return visitSymbol(symbol); + } + + @Override + default Symbol visit(Code symbol) { + return visitSymbol(symbol); + } + + @Override + default Symbol visit(Conditional symbol) { + return visitSymbol(symbol); + } + + @Override + default Symbol visit(IfThen symbol) { + return visitSymbol(symbol); + } + + @Override + default Symbol visit(IfThenElse symbol) { + return visitSymbol(symbol); + } + + @Override + default Symbol visit(Ignore symbol) { + return visitSymbol(symbol); + } + + @Override + default Symbol visit(Nonterminal symbol) { + return visitSymbol(symbol); + } + + @Override + default Symbol visit(Offside symbol) { + return visitSymbol(symbol); + } + + @Override + default Symbol visit(Terminal symbol) { + return visitSymbol(symbol); + } + + @Override + default Symbol visit(While symbol) { + return visitSymbol(symbol); + } + + @Override + default Symbol visit(Return symbol) { + return visitSymbol(symbol); + } + + @Override + default Symbol visit(Alt symbol) { + return visitSymbol(symbol); + } + + @Override + default Symbol visit(Opt symbol) { + return visitSymbol(symbol); + } + + @Override + default Symbol visit(Plus symbol) { + return visitSymbol(symbol); + } + + @Override + default Symbol visit(Group symbol) { + return visitSymbol(symbol); + } + + @Override + default Symbol visit(Star symbol) { + return visitSymbol(symbol); + } + + @Override + default Symbol visit(Start symbol) { + return visitSymbol(symbol); + } + + @Override + default Symbol visit(Identifier symbol) { + return symbol; + } + + @Override + default Condition visit(DataDependentCondition condition) { + return condition; + } + + @Override + default Condition visit(PositionalCondition condition) { + return condition; + } + + @Override + default Condition visit(RegularExpressionCondition condition) { + RegularExpression newRegex = condition.getRegularExpression().accept(this); + Condition newCondition = new RegularExpressionCondition(condition.getType(), newRegex); + if (newCondition.equals(condition)) { + return condition; + } else { + return newCondition; + } + } + + @Override + default RegularExpression visit(Char c) { + return c; + } + + @Override + default RegularExpression visit(CharRange r) { + return r; + } + + @Override + default RegularExpression visit(EOF eof) { + return eof; + } + + @Override + default RegularExpression visit(Epsilon e) { + return e; + } + + @Override + default RegularExpression visit(org.iguana.regex.Star s) { + return visitRegularExpression(s); + } + + @Override + default RegularExpression visit(org.iguana.regex.Plus p) { + return visitRegularExpression(p); + } + + @Override + default RegularExpression visit(org.iguana.regex.Opt o) { + return visitRegularExpression(o); + } + + @Override + default RegularExpression visit(org.iguana.regex.Alt alt) { + return visitRegularExpression(alt); + } + + @Override + default RegularExpression visit(org.iguana.regex.Seq seq) { + return visitRegularExpression(seq); + } + + @Override + default RegularExpression visit(Reference ref) { + return ref; + } + + @Override + default Symbol visit(CodeHolder symbol) { + return symbol; + } + + @Override + default Symbol visit(Error error) { + return error; + } + + default Symbol visitSymbol(Symbol symbol) { + List newChildren = symbol.getChildren().stream().map(s -> s.accept(this)).collect(Collectors.toList()); + Symbol newSymbol = symbol.copy() + .setChildren(newChildren) + .setPreConditions(visitPreConditions(symbol)) + .setPostConditions(visitPostConditions(symbol)) + .build(); + + if (newSymbol.equals(symbol)) { + return symbol; + } else { + return newSymbol; + } + } + + default List visitPreConditions(Symbol symbol) { + List preConditions = new ArrayList<>(); + for (Condition condition : symbol.getPreConditions()) { + preConditions.add(condition.accept(this)); + } + return preConditions; + } + + default List visitPostConditions(Symbol symbol) { + List postConditions = new ArrayList<>(); + for (Condition condition : symbol.getPostConditions()) { + postConditions.add(condition.accept(this)); + } + return postConditions; + } + + default RegularExpression visitRegularExpression(RegularExpression regex) { + List newChildren = regex.getChildren().stream().map(r -> r.accept(this)) + .collect(Collectors.toList()); + RegularExpression newRegex = regex.copy().setChildren(newChildren).build(); + if (newRegex.equals(regex)) { + return regex; + } else { + return newRegex; + } + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/traversal/ToSlotActionConditionVisitor.java b/benchmarks/src/main/kotlin/org/iguana/traversal/ToSlotActionConditionVisitor.java new file mode 100644 index 000000000..8edbc62f3 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/traversal/ToSlotActionConditionVisitor.java @@ -0,0 +1,279 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.traversal; + +import org.iguana.datadependent.env.IEvaluatorContext; +import org.iguana.grammar.condition.DataDependentCondition; +import org.iguana.grammar.condition.PositionalCondition; +import org.iguana.grammar.condition.RegularExpressionCondition; +import org.iguana.grammar.condition.SlotAction; +import org.iguana.grammar.slot.BodyGrammarSlot; +import org.iguana.gss.GSSNode; +import org.iguana.regex.matcher.Matcher; +import org.iguana.regex.matcher.MatcherFactory; +import org.iguana.result.Result; +import org.iguana.utils.input.Input; + +import java.util.HashMap; +import java.util.Map; + +public class ToSlotActionConditionVisitor implements IConditionVisitor { + + private final MatcherFactory factory; + + private final Map cachePositional = new HashMap<>(); + + private final Map cacheRegular = new HashMap<>(); + + public ToSlotActionConditionVisitor(MatcherFactory factory) { + this.factory = factory; + } + + @Override + public SlotAction visit(DataDependentCondition condition) { + return new SlotAction() { + + @Override + public boolean execute( + Input input, + BodyGrammarSlot slot, + GSSNode gssNode, + int leftExtent, + int rightExtent, + IEvaluatorContext ctx) { + Object value = condition.getExpression().interpret(ctx, input); + if (!(value instanceof Boolean)) + throw new RuntimeException("Data dependent condition should evaluate to a boolean value."); + return (!(Boolean) value); + } + + @Override + public String toString() { + return condition.toString(); + } + }; + } + + @Override + public SlotAction visit(PositionalCondition condition) { + return cachePositional.computeIfAbsent(condition, ToSlotActionConditionVisitor::create); + } + + private static SlotAction create(PositionalCondition condition) { + switch (condition.getType()) { + case START_OF_LINE: + return new SlotAction() { + + @Override + public boolean execute( + Input input, + BodyGrammarSlot slot, + GSSNode gssNode, + int leftExtent, + int rightExtent, + IEvaluatorContext ctx) { + return !input.isStartOfLine(rightExtent); + } + + @Override + public String toString() { + return condition.getType().getDescription(); + } + }; + + + case END_OF_LINE: + return new SlotAction() { + + @Override + public boolean execute( + Input input, + BodyGrammarSlot slot, + GSSNode gssNode, + int leftExtent, + int rightExtent, + IEvaluatorContext ctx) { + return !input.isEndOfLine(rightExtent); + } + + @Override + public String toString() { + return condition.getType().getDescription(); + } + }; + + case END_OF_FILE: + return new SlotAction() { + + @Override + public boolean execute( + Input input, + BodyGrammarSlot slot, + GSSNode gssNode, + int leftExtent, + int rightExtent, + IEvaluatorContext ctx) { + return !input.isEndOfFile(rightExtent); + } + + @Override + public String toString() { + return condition.getType().getDescription(); + } + }; + + + default: + throw new RuntimeException(); + } + } + + @Override + public SlotAction visit(RegularExpressionCondition condition) { + return cacheRegular.computeIfAbsent(condition, c -> create(c, factory)); + } + + private static SlotAction create(RegularExpressionCondition condition, MatcherFactory factory) { + + switch (condition.getType()) { + case FOLLOW: + case FOLLOW_IGNORE_LAYOUT: + return new SlotAction() { + final Matcher matcher = factory.getMatcher(condition.getRegularExpression()); + + @Override + public boolean execute( + Input input, + BodyGrammarSlot slot, + GSSNode gssNode, + int leftExtent, + int rightExtent, + IEvaluatorContext ctx) { + return matcher.match(input, rightExtent) == -1; + } + + @Override + public String toString() { + return condition.getType().getDescription(); + } + }; + + case NOT_FOLLOW: + case NOT_FOLLOW_IGNORE_LAYOUT: + return new SlotAction() { + final Matcher matcher = factory.getMatcher(condition.getRegularExpression()); + + @Override + public boolean execute( + Input input, + BodyGrammarSlot slot, + GSSNode gssNode, + int leftExtent, + int rightExtent, + IEvaluatorContext ctx) { + return matcher.match(input, rightExtent) >= 0; + } + + @Override + public String toString() { + return condition.getType().toString(); + } + }; + + case MATCH: + throw new RuntimeException("Unsupported"); + + case NOT_MATCH: + return new SlotAction() { + final Matcher matcher = factory.getMatcher(condition.getRegularExpression()); + + @Override + public boolean execute( + Input input, + BodyGrammarSlot slot, + GSSNode gssNode, + int leftExtent, + int rightExtent, + IEvaluatorContext ctx) { + return matcher.match(input, gssNode.getInputIndex(), rightExtent); + } + + @Override + public String toString() { + return condition.getType().toString(); + } + }; + + case NOT_PRECEDE: + return new SlotAction() { + final Matcher matcher = factory.getBackwardsMatcher(condition.getRegularExpression()); + + @Override + public boolean execute( + Input input, + BodyGrammarSlot slot, + GSSNode gssNode, + int leftExtent, + int rightExtent, + IEvaluatorContext ctx) { + return matcher.match(input, rightExtent) >= 0; + } + + @Override + public String toString() { + return condition.getType().toString(); + } + }; + + case PRECEDE: + return new SlotAction() { + final Matcher matcher = factory.getBackwardsMatcher(condition.getRegularExpression()); + + @Override + public boolean execute( + Input input, + BodyGrammarSlot slot, + GSSNode gssNode, + int leftExtent, + int rightExtent, + IEvaluatorContext ctx) { + return matcher.match(input, rightExtent) == -1; + } + + @Override + public String toString() { + return condition.getType().toString(); + } + }; + + default: + throw new RuntimeException("Unexpected error occurred."); + } + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/traversal/exception/AmbiguityException.java b/benchmarks/src/main/kotlin/org/iguana/traversal/exception/AmbiguityException.java new file mode 100644 index 000000000..8397f05aa --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/traversal/exception/AmbiguityException.java @@ -0,0 +1,29 @@ +package org.iguana.traversal.exception; + +import org.iguana.sppf.NonPackedNode; +import org.iguana.utils.input.Input; + +@SuppressWarnings("serial") +public class AmbiguityException extends RuntimeException { + + private final NonPackedNode node; + private final Input input; + + public AmbiguityException(NonPackedNode node, Input input) { + this.node = node; + this.input = input; + } + + public NonPackedNode getNode() { + return node; + } + + @Override + public String toString() { + int lineNumber = input.getLineNumber(node.getRightExtent()); + int columnNumber = input.getColumnNumber(node.getRightExtent()); + String ambiguousSubstring = input.subString(node.getLeftExtent(), node.getRightExtent()); + return String.format("Ambiguity found for node %s at (%d, %d): '%s'", node, lineNumber, columnNumber, + ambiguousSubstring); + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/traversal/exception/CyclicGrammarException.java b/benchmarks/src/main/kotlin/org/iguana/traversal/exception/CyclicGrammarException.java new file mode 100644 index 000000000..2169ba56a --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/traversal/exception/CyclicGrammarException.java @@ -0,0 +1,24 @@ +package org.iguana.traversal.exception; + +import org.iguana.grammar.symbol.Nonterminal; + +import java.util.List; + +@SuppressWarnings("serial") +public class CyclicGrammarException extends RuntimeException { + + private final List cycle; + + public CyclicGrammarException(List cycle) { + this.cycle = cycle; + } + + public List getCycle() { + return cycle; + } + + @Override + public String toString() { + return "The SPPF is cyclic, no getParserTree tree can be produced."; + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/traversal/idea/CollectRegularExpressions.java b/benchmarks/src/main/kotlin/org/iguana/traversal/idea/CollectRegularExpressions.java new file mode 100644 index 000000000..a29dea354 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/traversal/idea/CollectRegularExpressions.java @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.traversal.idea; + +import org.iguana.grammar.runtime.RuntimeGrammar; +import org.iguana.grammar.runtime.RuntimeRule; +import org.iguana.grammar.slot.TerminalNodeType; +import org.iguana.grammar.symbol.Align; +import org.iguana.grammar.symbol.Alt; +import org.iguana.grammar.symbol.Block; +import org.iguana.grammar.symbol.Code; +import org.iguana.grammar.symbol.Conditional; +import org.iguana.grammar.symbol.Error; +import org.iguana.grammar.symbol.Group; +import org.iguana.grammar.symbol.IfThen; +import org.iguana.grammar.symbol.IfThenElse; +import org.iguana.grammar.symbol.Ignore; +import org.iguana.grammar.symbol.Nonterminal; +import org.iguana.grammar.symbol.Offside; +import org.iguana.grammar.symbol.Opt; +import org.iguana.grammar.symbol.Plus; +import org.iguana.grammar.symbol.Return; +import org.iguana.grammar.symbol.Star; +import org.iguana.grammar.symbol.Start; +import org.iguana.grammar.symbol.Symbol; +import org.iguana.grammar.symbol.Terminal; +import org.iguana.grammar.symbol.While; +import org.iguana.regex.RegularExpression; +import org.iguana.traversal.ISymbolVisitor; + +import java.util.Map; + +/** + * Created by Anastasia Izmaylova on 17/12/15. + */ + +/* + * Collects regular expressions by type: named, keywords, the rest. + */ +public class CollectRegularExpressions implements ISymbolVisitor { + + private final Map terminals; + + public CollectRegularExpressions(Map terminals) { + this.terminals = terminals; + } + + public void collect(RuntimeGrammar grammar) { + for (RuntimeRule rule : grammar.getRules()) { + for (Symbol symbol : rule.getBody()) + symbol.accept(this); + } + + if (grammar.getLayout() instanceof Terminal) + grammar.getLayout().accept(this); + } + + @Override + public Void visit(Align symbol) { + return symbol.getSymbol().accept(this); + } + + @Override + public Void visit(Block symbol) { + for (Symbol sym : symbol.getSymbols()) + sym.accept(this); + return null; + } + + @Override + public Void visit(Code symbol) { + return symbol.getSymbol().accept(this); + } + + @Override + public Void visit(Error error) { + return null; + } + + @Override + public Void visit(Conditional symbol) { + return symbol.getSymbol().accept(this); + } + + @Override + public Void visit(IfThen symbol) { + return symbol.getThenPart().accept(this); + } + + @Override + public Void visit(IfThenElse symbol) { + symbol.getThenPart().accept(this); + return symbol.getElsePart().accept(this); + } + + @Override + public Void visit(Ignore symbol) { + return symbol.getSymbol().accept(this); + } + + @Override + public Void visit(Nonterminal symbol) { + return null; + } + + @Override + public Void visit(Offside symbol) { + return symbol.getSymbol().accept(this); + } + + @Override + public Void visit(Terminal symbol) { + RegularExpression regex = symbol.getRegularExpression(); + + if (symbol.getNodeType() == TerminalNodeType.Regex) { + terminals.put("|regex|:" + symbol.getName(), regex); + } else if (symbol.getNodeType() == TerminalNodeType.Literal) + terminals.put("|keyword|:" + symbol.getName(), regex); + else + terminals.put(symbol.getName(), regex); + + return null; + } + + @Override + public Void visit(While symbol) { + return symbol.getBody().accept(this); + } + + @Override + public Void visit(Return symbol) { + return null; + } + + @Override + public Void visit(Alt symbol) { + for (Symbol sym : symbol.getSymbols()) + sym.accept(this); + return null; + } + + @Override + public Void visit(Opt symbol) { + return symbol.getSymbol().accept(this); + } + + @Override + public Void visit(Plus symbol) { + for (Symbol sep : symbol.getSeparators()) + sep.accept(this); + return symbol.getSymbol().accept(this); + } + + @Override + public Void visit(Group symbol) { + for (Symbol sym : symbol.getSymbols()) + sym.accept(this); + return null; + } + + @Override + public Void visit(Star symbol) { + for (Symbol sep : symbol.getSeparators()) + sep.accept(this); + return symbol.getSymbol().accept(this); + } + + @Override + public Void visit(Start start) { + return null; +// return start.getNonterminal().accept(this); + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/traversal/idea/GenerateBasicFiles.java b/benchmarks/src/main/kotlin/org/iguana/traversal/idea/GenerateBasicFiles.java new file mode 100644 index 000000000..9f0008176 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/traversal/idea/GenerateBasicFiles.java @@ -0,0 +1,183 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.traversal.idea; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.PrintWriter; +import java.io.UnsupportedEncodingException; + +/** + * Created by Anastasia Izmaylova on 17/12/15. + */ +public class GenerateBasicFiles { + /* + * Lang.java + * FileType.java + * FileTypeFactory.java + * ElementType.java + * TokenType.java + */ + // CHECKSTYLE:OFF LineLength + public static void generate(String language, String extension, String path) { + // Create the respective directories + String name = path + language.toLowerCase(); + new File(name).mkdir(); + name = path + language.toLowerCase() + "/gen"; + new File(name).mkdir(); + name = path + language.toLowerCase() + "/gen/lang"; + new File(name).mkdir(); + name = path + language.toLowerCase() + "/gen/icons"; + new File(name).mkdir(); + name = path + language.toLowerCase() + "/gen/psi"; + new File(name).mkdir(); + + File file = new File(path + language.toLowerCase() + "/gen/lang/" + language + "Lang.java"); + try { + PrintWriter writer = new PrintWriter(file.getAbsolutePath(), "UTF-8"); + writer.println("package " + language.toLowerCase() + ".gen.lang;"); + writer.println(); + writer.println("/** This file has been generated. */"); + writer.println(); + writer.println("import com.intellij.lang.Language;"); + writer.println("import com.intellij.openapi.util.IconLoader;"); + writer.println("import javax.swing.*;"); + writer.println(); + writer.println("public class " + language + "Lang extends Language {"); + writer.println(" public static final " + language + "Lang instance = new " + language + "Lang();"); + writer.println(" public static final Icon file = IconLoader.getIcon(\"/" + language.toLowerCase() + + "/gen/icons/icon.png\");"); + writer.println(" private " + language + "Lang() { super(\"" + language + "\"); }"); + writer.println("}"); + writer.println(); + writer.close(); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + + file = new File(path + language.toLowerCase() + "/gen/lang/" + language + "FileType.java"); + try { + PrintWriter writer = new PrintWriter(file.getAbsolutePath(), "UTF-8"); + writer.println("package " + language.toLowerCase() + ".gen.lang;"); + writer.println(); + writer.println("/** This file has been generated. */"); + writer.println(); + writer.println("import com.intellij.openapi.fileTypes.LanguageFileType;"); + writer.println("import javax.swing.*;"); + writer.println(); + writer.println("public class " + language + "FileType extends LanguageFileType {"); + writer.println( + " public static final " + language + "FileType instance = new " + language + "FileType();"); + writer.println(" private " + language + "FileType() { super(" + language + "Lang.instance); }"); + writer.println(); + writer.println(" public String getName() { return \"" + language + "\"; }"); + writer.println(" public String getDescription() { return \"" + language + "\"; }"); + writer.println(" public String getDefaultExtension() { return \"" + extension + "\"; }"); + writer.println(" public Icon getIcon() { return " + language + "Lang.file; }"); + writer.println("}"); + writer.println(); + writer.close(); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + + file = new File(path + language.toLowerCase() + "/gen/lang/" + language + "FileTypeFactory.java"); + try { + PrintWriter writer = new PrintWriter(file.getAbsolutePath(), "UTF-8"); + writer.println("package " + language.toLowerCase() + ".gen.lang;"); + writer.println(); + writer.println("/** This file has been generated. */"); + writer.println(); + writer.println("import com.intellij.openapi.fileTypes.FileTypeConsumer;"); + writer.println("import com.intellij.openapi.fileTypes.FileTypeFactory;"); + writer.println(); + writer.println("public class " + language + "FileTypeFactory extends FileTypeFactory {"); + writer.println( + " public void createFileTypes(FileTypeConsumer fileTypeConsumer) { fileTypeConsumer.consume(" + + language + "FileType.instance, \"" + extension + "\"); }"); + writer.println("}"); + writer.println(); + writer.close(); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + + file = new File(path + language.toLowerCase() + "/gen/psi/" + language + "ElementType.java"); + try { + PrintWriter writer = new PrintWriter(file.getAbsolutePath(), "UTF-8"); + writer.println("package " + language.toLowerCase() + ".gen.psi;"); + writer.println(); + writer.println("/** This file has been generated. */"); + writer.println(); + writer.println("import com.intellij.psi.tree.IElementType;"); + writer.println("import " + language.toLowerCase() + ".gen.lang." + language + "Lang;"); + writer.println(); + writer.println("public class " + language + "ElementType extends IElementType {"); + writer.println(" public " + language + "ElementType(String debugName) { super(debugName, " + language + + "Lang.instance); }"); + writer.println("}"); + writer.println(); + writer.close(); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + + file = new File(path + language.toLowerCase() + "/gen/psi/" + language + "TokenType.java"); + try { + PrintWriter writer = new PrintWriter(file.getAbsolutePath(), "UTF-8"); + writer.println("package " + language.toLowerCase() + ".gen.psi;"); + writer.println(); + writer.println("/** This file has been generated. */"); + writer.println(); + writer.println("import com.intellij.psi.tree.IElementType;"); + writer.println("import " + language.toLowerCase() + ".gen.lang." + language + "Lang;"); + writer.println(); + writer.println("public class " + language + "TokenType extends IElementType {"); + writer.println(" public " + language + "TokenType(String debugName) { super(debugName, " + language + + "Lang.instance); }"); + writer.println( + " public String toString() { return \"" + language + "TokenType.\" + super.toString(); }"); + writer.println("}"); + writer.println(); + writer.close(); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + } + // CHECKSTYLE:ON LineLength +} diff --git a/benchmarks/src/main/kotlin/org/iguana/traversal/idea/GenerateBasicHighlighter.java b/benchmarks/src/main/kotlin/org/iguana/traversal/idea/GenerateBasicHighlighter.java new file mode 100644 index 000000000..42a794db0 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/traversal/idea/GenerateBasicHighlighter.java @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.traversal.idea; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.PrintWriter; +import java.io.UnsupportedEncodingException; +import java.util.Set; + +/** + * Created by Anastasia Izmaylova on 18/12/15. + */ +public class GenerateBasicHighlighter { + /* + * SyntaxHighlighter.java + * SyntaxHighlighterFactory.java + */ + public static void generate(String language, String path, Set tokenTypes) { + new File(path + language.toLowerCase() + "/gen/editor").mkdir(); + File file = new File(path + language.toLowerCase() + "/gen/editor/" + language + "SyntaxHighlighter.java"); + try { + PrintWriter writer = new PrintWriter(file.getAbsolutePath(), "UTF-8"); + writer.println("package " + language.toLowerCase() + ".gen.editor;"); + writer.println(); + writer.println("/** This file has been generated. */"); + writer.println(); + writer.println("import com.intellij.lexer.Lexer;"); + writer.println("import com.intellij.openapi.editor.DefaultLanguageHighlighterColors;"); + writer.println("import com.intellij.openapi.editor.HighlighterColors;"); + writer.println("import com.intellij.openapi.editor.colors.TextAttributesKey;"); + writer.println("import com.intellij.openapi.fileTypes.SyntaxHighlighterBase;"); + writer.println("import com.intellij.psi.tree.IElementType;"); + writer.println("import " + language.toLowerCase() + ".gen.lexer." + language + "Lexer;"); + writer.println("import " + language.toLowerCase() + ".gen.psi." + language + "TokenTypes;"); + writer.println(); + writer.println("public class " + language + "SyntaxHighlighter extends SyntaxHighlighterBase {"); + writer.println(); + writer.println(" public Lexer getHighlightingLexer() { return new " + language + "Lexer(); }"); + writer.println(); + writer.println(" public TextAttributesKey[] getTokenHighlights(IElementType tokenType) {"); + int i = 0; + for (String tokenType : tokenTypes) { + String color = getColor(tokenType); + if (color != null) { + if (color.equals("BAD_CHARACTER")) + color = "HighlighterColors.TEXT"; + else + color = "DefaultLanguageHighlighterColors." + color; + writer.println((i == 0 ? " if " : " else if ") + "(tokenType.equals(" + language + + "TokenTypes." + tokenType + "))"); + writer.println( + " return new TextAttributesKey[] {TextAttributesKey.createTextAttributesKey(\"" + + tokenType + "\", " + color + ")};"); + i++; + } + } + writer.println(" return new TextAttributesKey[0];"); + writer.println(" }"); + writer.println("}"); + writer.println(); + writer.close(); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + + file = new File(path + language.toLowerCase() + "/gen/editor/" + language + "SyntaxHighlighterFactory.java"); + try { + PrintWriter writer = new PrintWriter(file.getAbsolutePath(), "UTF-8"); + writer.println("package " + language.toLowerCase() + ".gen.editor;"); + writer.println(); + writer.println("/** This file has been generated. */"); + writer.println(); + writer.println("import com.intellij.openapi.fileTypes.SyntaxHighlighter;"); + writer.println("import com.intellij.openapi.fileTypes.SyntaxHighlighterFactory;"); + writer.println("import com.intellij.openapi.project.Project;"); + writer.println("import com.intellij.openapi.vfs.VirtualFile;"); + writer.println(); + writer.println("public class " + language + "SyntaxHighlighterFactory extends SyntaxHighlighterFactory {"); + writer.println( + " public SyntaxHighlighter getSyntaxHighlighter(Project project, VirtualFile virtualFile) { " + + "return new " + language + "SyntaxHighlighter(); }"); + writer.println("}"); + writer.println(); + writer.close(); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + } + + private static String getColor(String tokenType) { + switch (tokenType) { + case "OPERATOR": + return "OPERATION_SIGN"; + case "OPEN_BRACE": + return "BRACES"; + case "CLOSE_BRACE": + return "BRACES"; + case "OPEN_PARENTHESIS": + return "PARENTHESES"; + case "CLOSE_PARENTHESIS": + return "PARENTHESES"; + case "OPEN_BRACKET": + return "BRACKETS"; + case "CLOSE_BRACKET": + return "BRACKETS"; + case "BAD_CHARACTER": + return "BAD_CHARACTER"; + default: + if (tokenType.toUpperCase().contains("COMMENT")) + return "LINE_COMMENT"; + else + return "IDENTIFIER"; + } + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/traversal/idea/GenerateElements.java b/benchmarks/src/main/kotlin/org/iguana/traversal/idea/GenerateElements.java new file mode 100644 index 000000000..b8f5adb75 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/traversal/idea/GenerateElements.java @@ -0,0 +1,949 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.traversal.idea; + +import org.iguana.grammar.runtime.RuntimeRule; +import org.iguana.grammar.symbol.Align; +import org.iguana.grammar.symbol.Alt; +import org.iguana.grammar.symbol.Block; +import org.iguana.grammar.symbol.Code; +import org.iguana.grammar.symbol.Conditional; +import org.iguana.grammar.symbol.Error; +import org.iguana.grammar.symbol.Group; +import org.iguana.grammar.symbol.IfThen; +import org.iguana.grammar.symbol.IfThenElse; +import org.iguana.grammar.symbol.Ignore; +import org.iguana.grammar.symbol.Nonterminal; +import org.iguana.grammar.symbol.Offside; +import org.iguana.grammar.symbol.Opt; +import org.iguana.grammar.symbol.Plus; +import org.iguana.grammar.symbol.Return; +import org.iguana.grammar.symbol.Star; +import org.iguana.grammar.symbol.Start; +import org.iguana.grammar.symbol.Symbol; +import org.iguana.grammar.symbol.Terminal; +import org.iguana.grammar.symbol.While; +import org.iguana.traversal.ISymbolVisitor; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.PrintWriter; +import java.io.UnsupportedEncodingException; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * Created by Anastasia Izmaylova on 18/12/15. + */ + +public class GenerateElements { + /* + * ElementTypes.java + * gen.psi.* + * gen.psi.impl.* + */ + + enum NUM { + ONE, + MORE_THAN_ONE, + ONE_AND_MORE + } + + public static void generate(List rules, String language, String path) { + Map> elements = new LinkedHashMap<>(); + for (RuntimeRule rule : rules) { + + Set labels = elements.get(rule.getHead().getName()); + if (labels == null) { + labels = new HashSet<>(); + elements.put(rule.getHead().getName(), labels); + } + labels.add(rule.getLabel() == null ? "Impl" : rule.getLabel()); + } + generateElementTypes(elements, language, path); + generatePhiElements(rules, language, path); + } + + // CHECKSTYLE:OFF OperatorWrap + private static void generateElementTypes(Map> elements, String language, String path) { + File file = new File(path + language.toLowerCase() + "/gen/psi/" + language + "ElementTypes.java"); + try { + PrintWriter writer = new PrintWriter(file.getAbsolutePath(), "UTF-8"); + writer.println("package " + language.toLowerCase() + ".gen.psi;"); + writer.println(); + writer.println("/* This file has been generated. */"); + writer.println(); + writer.println("import com.intellij.lang.ASTNode;"); + writer.println("import com.intellij.psi.PsiElement;"); + writer.println("import com.intellij.psi.tree.IElementType;"); + writer.println("import " + language.toLowerCase() + ".gen.psi." + language + "ElementType;"); + writer.println("import " + language.toLowerCase() + ".gen.psi.impl.*;"); + writer.println(); + writer.println("public interface " + language + "ElementTypes {"); + writer.println(); + // ebnf related types, also data-dependent + // * and while + writer.println(" public IElementType LIST = new " + language + "ElementType(\"LIST\");"); + // ? and if-then + writer.println(" public IElementType OPT = new " + language + "ElementType(\"OPT\");"); + // | and if-then-else + writer.println(" public IElementType ALT = new " + language + "ElementType(\"ALT\");"); + // () and {} + writer.println(" public IElementType SEQ = new " + language + "ElementType(\"SEQ\");"); + writer.println(); + for (String head : elements.keySet()) { + for (String label : elements.get(head)) { + if (label.equals("Impl")) + writer.println(" public IElementType " + head.toUpperCase() + " = new " + language + + "ElementType(\"" + head.toUpperCase() + "\");"); + else + writer.println( + " public IElementType " + head.toUpperCase() + "_" + label.toUpperCase() + " = new " + + language + "ElementType(\"" + head.toUpperCase() + "_" + label.toUpperCase() + "\");"); + } + } + writer.println(); + writer.println(" public static IElementType get(String name) {"); + writer.println(" switch (name) {"); + writer.println(" case \"LIST\": return LIST;"); + writer.println(" case \"OPT\": return OPT;"); + writer.println(" case \"ALT\": return ALT;"); + writer.println(" case \"SEQ\": return SEQ;"); + for (String head : elements.keySet()) { + for (String label : elements.get(head)) { + if (label.equals("Impl")) writer.println( + " case \"" + head.toUpperCase() + "\": return " + head.toUpperCase() + ";"); + else + writer.println( + " case \"" + head.toUpperCase() + "_" + label.toUpperCase() + "\": return " + + head.toUpperCase() + "_" + label.toUpperCase() + ";"); + } + } + writer.println(" }"); + writer.println(" throw new RuntimeException(\"Should not have happened!\");"); + writer.println(" }"); + writer.println(); + writer.println(" class Factory {"); + writer.println(" public static PsiElement createElement(ASTNode node) {"); + writer.println(" IElementType type = node.getElementType();"); + writer.println(" if (type == LIST || type == OPT || type == ALT || type == SEQ) " + + "return new EbnfElementImpl(node);"); + for (String head : elements.keySet()) { + for (String label : elements.get(head)) { + if (label.equals("Impl")) + writer.println( + " if (type == " + head.toUpperCase() + ") return new " + head + "Impl(node);"); + else + writer.println(" if (type == " + head.toUpperCase() + "_" + label.toUpperCase() + + ") return new " + head + label + "Impl(node);"); + } + } + writer.println(" throw new RuntimeException(\"Should not have happened!\");"); + writer.println(" }"); + writer.println(" }"); + writer.println("}"); + writer.close(); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + } + // CHECKSTYLE:ON OperatorWrap + + private static void generatePhiElements(List rules, String language, String path) { + new File(path + language.toLowerCase() + "/gen/psi/impl").mkdir(); + // Symbol names with their occurrence counter; per nonterminal and per label of a rule + Map>> elements = new LinkedHashMap<>(); + + for (RuntimeRule rule : rules) { + Map> m1 = elements.get(rule.getHead().getName()); + if (m1 == null) { + m1 = new LinkedHashMap<>(); + elements.put(rule.getHead().getName(), m1); + } + + String label = rule.getLabel(); + if (label == null || label.isEmpty()) label = "Impl"; + + Map m2 = m1.get(label); + + Map m3 = new LinkedHashMap<>(); + new GetPhiElements(rule, m3).compute(language, path); + + if (m2 == null) { + m1.put(label, m3); + continue; + } + + for (Map.Entry entry : m3.entrySet()) { + NUM num = m2.get(entry.getKey()); + if (num == null) + m2.put(entry.getKey(), entry.getValue()); + else + switch (num) { + case ONE: + if (entry.getValue() != NUM.ONE) + num = entry.getValue(); + break; + case MORE_THAN_ONE: + break; + case ONE_AND_MORE: + throw new RuntimeException("Should not happen!"); + } + } + } + + GetPhiElements.generate(elements, language, path); + } + + private static class GetPhiElements implements ISymbolVisitor { + + private static final InferPsiEbnfElementType typer = new InferPsiEbnfElementType(); + + private final RuntimeRule rule; + private final Map children; + + GetPhiElements(RuntimeRule rule, Map children) { + this.rule = rule; + this.children = children; + } + + public void compute(String language, String path) { + String prev_ebnf_element = null; + for (Symbol symbol : rule.getBody()) { + String child = symbol.accept(this); + if (child != null) { + + if (child.endsWith("$Ebnf")) { + if (prev_ebnf_element != null && !prev_ebnf_element.equals(child)) { + if (!prev_ebnf_element.equals("Element$Ebnf")) { + children.remove(prev_ebnf_element); + prev_ebnf_element = "Element$Ebnf"; + children.put(prev_ebnf_element, NUM.MORE_THAN_ONE); + } else if (prev_ebnf_element.equals("Element$Ebnf")) { + NUM num = children.get(prev_ebnf_element); + if (num == NUM.ONE) + children.put(prev_ebnf_element, NUM.MORE_THAN_ONE); + } + continue; + } else + prev_ebnf_element = child; + } + + NUM num = children.get(child); + if (num == null) + num = NUM.ONE; + else + switch (num) { + case ONE: + num = NUM.MORE_THAN_ONE; + break; + case MORE_THAN_ONE: + break; + default: + throw new RuntimeException("Should not happen!"); + } + children.put(child, num); + } + } + } + + @Override + public String visit(Align symbol) { + return symbol.getSymbol().accept(this); + } + + @Override + public String visit(Block symbol) { + String type = typer.visit(symbol); + if (type == null) return null; + if (type.equals("PsiElement")) return "Element$Ebnf"; + return type + "$Ebnf"; + } + + @Override + public String visit(Code symbol) { + return symbol.getSymbol().accept(this); + } + + @Override + public String visit(Error error) { + return null; + } + + @Override + public String visit(Conditional symbol) { + return symbol.getSymbol().accept(this); + } + + @Override + public String visit(IfThen symbol) { + String type = typer.visit(symbol); + if (type == null) return null; + if (type.equals("PsiElement")) return "Element$Ebnf"; + return type + "$Ebnf"; + } + + @Override + public String visit(IfThenElse symbol) { + String type = typer.visit(symbol); + if (type == null) return null; + if (type.equals("PsiElement")) return "Element$Ebnf"; + return type + "$Ebnf"; + } + + @Override + public String visit(Ignore symbol) { + return symbol.getSymbol().accept(this); + } + + @Override + public String visit(Nonterminal symbol) { + return symbol.getName(); + } + + @Override + public String visit(Offside symbol) { + return symbol.getSymbol().getName(); + } + + @Override + public String visit(Terminal symbol) { + return null; + } + + @Override + public String visit(While symbol) { + String type = typer.visit(symbol); + if (type == null) return null; + if (type.equals("PsiElement")) return "Element$Ebnf"; + return type + "$Ebnf"; + } + + @Override + public String visit(Return symbol) { + return null; + } + + @Override + public String visit(Alt symbol) { + String type = typer.visit(symbol); + if (type == null) return null; + if (type.equals("PsiElement")) return "Element$Ebnf"; + return type + "$Ebnf"; + } + + @Override + public String visit(Opt symbol) { + String type = typer.visit(symbol); + if (type == null) return null; + if (type.equals("PsiElement")) return "Element$Ebnf"; + return type + "$Ebnf"; + } + + @Override + public String visit(Plus symbol) { + String type = typer.visit(symbol); + if (type == null) return null; + if (type.equals("PsiElement")) return "Element$Ebnf"; + return type + "$Ebnf"; + } + + @Override + public String visit(Group symbol) { + String type = typer.visit(symbol); + if (type == null) return null; + if (type.equals("PsiElement")) return "Element$Ebnf"; + return type + "$Ebnf"; + } + + @Override + public String visit(Star symbol) { + String type = typer.visit(symbol); + if (type == null) return null; + if (type.equals("PsiElement")) return "Element$Ebnf"; + return type + "$Ebnf"; + } + + @Override + public String visit(Start start) { + String type = typer.visit(start); + if (type == null) return null; + if (type.equals("PsiElement")) return "Element$Ebnf"; + return type + "$Ebnf"; + } + + public static void generate(Map>> elements, String language, String path) { + + File file = new File(path + language.toLowerCase() + "/gen/psi/IEbnfElement.java"); + try { + PrintWriter writer = new PrintWriter(file.getAbsolutePath(), "UTF-8"); + writer.println("package " + language.toLowerCase() + ".gen.psi;"); + writer.println(); + writer.println("/* This file has been generated. */"); + writer.println(); + writer.println("import com.intellij.psi.PsiElement;"); + writer.println("import java.util.List;"); + writer.println(); + writer.println("public interface IEbnfElement" + " extends PsiElement {"); + writer.println(" public List" + " getElements" + "();"); + writer.println("}"); + writer.close(); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + + Map> elements_per_nt = new HashMap<>(); + + // Interfaces + for (String head : elements.keySet()) { + + Map symbols = new LinkedHashMap<>(); + elements_per_nt.put(head, symbols); + + for (Map m : elements.get(head).values()) { + for (Map.Entry entry : m.entrySet()) { + NUM num = symbols.get(entry.getKey()); + if (num == null) + num = entry.getValue(); + else + switch (num) { + case ONE: + if (entry.getValue() != NUM.ONE) + num = NUM.ONE_AND_MORE; + break; + case MORE_THAN_ONE: + if (entry.getValue() == NUM.ONE || entry.getValue() == NUM.ONE_AND_MORE) + num = NUM.ONE_AND_MORE; + break; + case ONE_AND_MORE: + break; + default: + } + symbols.put(entry.getKey(), num); + } + } + + file = new File(path + language.toLowerCase() + "/gen/psi/I" + head + ".java"); + + boolean declaration = head.endsWith("$Declaration"); + boolean reference = head.endsWith("$Reference"); + try { + PrintWriter writer = new PrintWriter(file.getAbsolutePath(), "UTF-8"); + writer.println("package " + language.toLowerCase() + ".gen.psi;"); + writer.println(); + writer.println("/* This file has been generated. */"); + writer.println(); + writer.println("import com.intellij.psi.PsiElement;"); + if (declaration) writer.println("import com.intellij.psi.PsiNamedElement;"); + if (reference) writer.println("import com.intellij.psi.PsiReference;"); + writer.println("import java.util.List;"); + writer.println(); + if (declaration) + writer.println("public interface I" + head + " extends PsiElement, PsiNamedElement {"); + else if (reference) + writer.println("public interface I" + head + " extends PsiElement, PsiReference {"); + else + writer.println("public interface I" + head + " extends PsiElement {"); + for (String symbol : symbols.keySet()) { + NUM num = symbols.get(symbol); + switch (num) { + case ONE: + if (symbol.endsWith("$Ebnf")) + writer.println( + " List get" + symbol.substring(0, symbol.lastIndexOf("$")) + + "List();"); + else + writer.println(" I" + symbol + " get" + symbol + "();"); + break; + case MORE_THAN_ONE: + if (symbol.endsWith("$Ebnf")) + writer.println(" List> getAll" + + symbol.substring(0, symbol.lastIndexOf("$")) + "List();"); + else + writer.println(" List getAll" + symbol + "();"); + break; + case ONE_AND_MORE: + if (symbol.endsWith("$Ebnf")) + writer.println( + " List get" + symbol.substring(0, symbol.lastIndexOf("$")) + + "List();"); + else + writer.println(" I" + symbol + " get" + symbol + "();"); + + if (symbol.endsWith("$Ebnf")) + writer.println(" List> getAll" + + symbol.substring(0, symbol.lastIndexOf("$")) + "List();"); + else + writer.println(" List getAll" + symbol + "();"); + break; + default: + } + } + writer.println("}"); + writer.close(); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + } + + // Classes + file = new File(path + language.toLowerCase() + "/gen/psi/impl/EbnfElementImpl.java"); + try { + PrintWriter writer = new PrintWriter(file.getAbsolutePath(), "UTF-8"); + writer.println("package " + language.toLowerCase() + ".gen.psi.impl;"); + writer.println(); + writer.println("/* This file has been generated. */"); + writer.println(); + writer.println("import com.intellij.psi.PsiElement;"); + writer.println("import com.intellij.psi.PsiElementVisitor;"); + writer.println("import com.intellij.psi.util.PsiTreeUtil;"); + writer.println(); + writer.println("import com.intellij.extapi.psi.ASTWrapperPsiElement;"); + writer.println("import com.intellij.lang.ASTNode;"); + writer.println(); + writer.println("import java.util.List;"); + writer.println("import java.util.ArrayList;"); + writer.println("import " + language.toLowerCase() + ".gen.psi.IEbnfElement;"); + writer.println(); + writer.println( + "public class EbnfElementImpl" + " extends ASTWrapperPsiElement implements IEbnfElement {"); + writer.println(); + writer.println(" public EbnfElementImpl(ASTNode node) { super(node); }"); + writer.println(); + writer.println(" public void accept(PsiElementVisitor visitor) { super.accept(visitor); }"); + writer.println(); + writer.println(" public List" + " getElements" + "() {"); + writer.println(" List flattened = new ArrayList<>();"); + writer.println( + " for (PsiElement e : PsiTreeUtil.getChildrenOfTypeAsList(this, PsiElement.class)) {"); + writer.println(" if (e instanceof IEbnfElement) "); + writer.println(" flattened.addAll(((IEbnfElement) e).getElements());"); + writer.println(" else "); + writer.println(" flattened.add(e);"); + writer.println(" }"); + writer.println(" return flattened;"); + writer.println(" }"); + writer.println("}"); + writer.close(); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + + for (String head : elements.keySet()) { + + Map symbols = elements_per_nt.get(head); + + for (Map.Entry> entry : elements.get(head).entrySet()) { + + String name = head + (entry.getKey().equals("Impl") ? entry.getKey() : entry.getKey() + "Impl"); + file = new File(path + language.toLowerCase() + "/gen/psi/impl/" + name + ".java"); + + boolean declaration = head.endsWith("$Declaration"); + boolean reference = head.endsWith("$Reference"); + try { + PrintWriter writer = new PrintWriter(file.getAbsolutePath(), "UTF-8"); + writer.println("package " + language.toLowerCase() + ".gen.psi.impl;"); + writer.println(); + writer.println("/* This file has been generated. */"); + writer.println(); + writer.println("import com.intellij.psi.PsiElement;"); + writer.println("import com.intellij.psi.PsiElementVisitor;"); + if (reference) writer.println("import com.intellij.psi.PsiReference;"); + writer.println("import com.intellij.psi.util.PsiTreeUtil;"); + writer.println(); + writer.println("import com.intellij.extapi.psi.ASTWrapperPsiElement;"); + writer.println("import com.intellij.lang.ASTNode;"); + if (reference) writer.println("import com.intellij.openapi.util.TextRange;"); + writer.println(); + if (declaration || reference) + writer.println("import com.intellij.util.IncorrectOperationException;"); + writer.println(); + if (declaration || reference) + writer.println( + "import " + language.toLowerCase() + ".gen.utils." + language + "ElementFactory;"); + if (reference) + writer.println("import " + language.toLowerCase() + ".gen.utils." + language + "Util;"); + writer.println(); + writer.println("import java.util.List;"); + writer.println("import java.util.ArrayList;"); + writer.println(); + writer.println("import " + language.toLowerCase() + ".gen.psi.*;"); + writer.println(); + writer.println( + "public class " + name + " extends ASTWrapperPsiElement implements I" + head + " {"); + writer.println(); + writer.println(" public " + name + "(ASTNode node) { super(node); }"); + writer.println(); + writer.println(" public void accept(PsiElementVisitor visitor) { super.accept(visitor); }"); + writer.println(); + + for (String symbol : symbols.keySet()) { + + NUM num1 = symbols.get(symbol); + NUM num2 = entry.getValue().get(symbol); + + switch (num1) { + case ONE: + if (num2 == null) { + if (symbol.endsWith("$Ebnf")) + writer.println(" public List get" + + symbol.substring(0, symbol.lastIndexOf("$")) + + "List() { return null; }"); + else + writer.println( + " public I" + symbol + " get" + symbol + "() { return null; }"); + } else { + if (symbol.endsWith("$Ebnf")) + writer.println(" public List get" + + symbol.substring(0, symbol.lastIndexOf("$")) + + "List() { return findNotNullChildByClass(" + + "IEbnfElement.class).getElements(); }"); + else + writer.println(" public I" + symbol + " get" + symbol + + "() { return findNotNullChildByClass(I" + symbol + + ".class); }"); + } + break; + case MORE_THAN_ONE: + if (num2 == null) { + if (symbol.endsWith("$Ebnf")) + writer.println(" public List> getAll" + + symbol.substring(0, symbol.lastIndexOf("$")) + + "List() { return null; }"); + else + writer.println(" public List getAll" + symbol + + "() { return null; }"); + } else { + if (symbol.endsWith("$Ebnf")) { + writer.println(" public List> getAll" + + symbol.substring(0, symbol.lastIndexOf("$")) + "List() {"); + writer.println( + " List> result = new ArrayList<>();"); + writer.println( + " for (IEbnfElement e : PsiTreeUtil.getChildrenOfTypeAsList(" + + "this, IEbnfElement.class))"); + writer.println(" result.add(e.getElements());"); + writer.println(" return result;"); + writer.println(" }"); + } else + writer.println(" public List getAll" + symbol + + "() { return PsiTreeUtil.getChildrenOfTypeAsList(this, I" + + symbol + ".class); }"); + } + break; + case ONE_AND_MORE: + if (num2 == null) { + if (symbol.endsWith("$Ebnf")) { + writer.println(" public List get" + + symbol.substring(0, symbol.lastIndexOf("$")) + + "List() { return null; }"); + writer.println(" public List> getAll" + + symbol.substring(0, symbol.lastIndexOf("$")) + + "List() { return null; }"); + } else { + writer.println( + " public I" + symbol + " get" + symbol + "() { return null; }"); + writer.println(" public List getAll" + symbol + + "() { return null; }"); + } + } else { + switch (num2) { + case ONE: + if (symbol.endsWith("$Ebnf")) { + writer.println(" public List get" + + symbol.substring(0, symbol.lastIndexOf("$")) + + "List() { return findNotNullChildByClass(" + + "IEbnfElement.class).getElements(); }"); + writer.println(" public List> getAll" + + symbol.substring(0, symbol.lastIndexOf("$")) + + "List() { return null; }"); + } else { + writer.println(" public I" + symbol + " get" + symbol + + "() { return findNotNullChildByClass(I" + symbol + + ".class); }"); + writer.println(" public List getAll" + symbol + + "() { return null; }"); + } + break; + case MORE_THAN_ONE: + case ONE_AND_MORE: + if (symbol.endsWith("$Ebnf")) { + writer.println(" public List get" + + symbol.substring(0, symbol.lastIndexOf("$")) + + "List() { return null; }"); + writer.println(" public List> getAll" + + symbol.substring(0, symbol.lastIndexOf("$")) + + "List() {"); + writer.println( + " List> result = new ArrayList<>();"); + writer.println( + " for (IEbnfElement e : " + + "PsiTreeUtil.getChildrenOfTypeAsList(this, " + + "IEbnfElement.class))"); + writer.println(" result.add(e.getElements());"); + writer.println(" return result;"); + writer.println(" }"); + } else { + writer.println(" public I" + symbol + " get" + symbol + + "() { return null; }"); + writer.println(" public List getAll" + symbol + + "() { return PsiTreeUtil.getChildrenOfTypeAsList" + + "(this, I" + + symbol + ".class); }"); + } + break; + } + } + break; + default: + } + } + + if (declaration) { + writer.println(); + writer.println(" public String getName() { return getResult().getText(); }"); + writer.println(); + writer.println( + " public PsiElement setName(String name) throws IncorrectOperationException {"); + writer.println(" ASTNode node = " + language + "ElementFactory.create" + + head.substring(0, head.length() - 12) + "(getProject(), name);"); + writer.println(" ASTNode first = getResult().getFirstChildNode();"); + writer.println(" getResult().replaceChild(first, node);"); + writer.println(" return this;"); + writer.println(" }"); + } + + if (reference) { + writer.println(); + writer.println( + " public PsiReference[] getReferences() { return new PsiReference[] {this}; }"); + writer.println(); + writer.println(" public PsiReference getReference() { return this; }"); + writer.println(); + writer.println(" public PsiElement getElement() { return this; }"); + writer.println(); + writer.println( + " public TextRange getRangeInElement() { return new TextRange(0, " + + "getTextLength()); }"); + writer.println(); + writer.println(" public PsiElement resolve() {"); + writer.println(" PsiElement element = " + language + "Util.find" + + head.substring(0, head.length() - 10) + "(getProject(), this);"); + writer.println(" return element;"); + writer.println(" }"); + writer.println(); + writer.println(" public String getCanonicalText() { return this.getText(); }"); + writer.println(); + writer.println( + " public PsiElement handleElementRename(String name) throws " + + "IncorrectOperationException {"); + writer.println(" ASTNode node = " + language + "ElementFactory.create" + + head.substring(0, head.length() - 10) + "(getProject(), name);"); + writer.println(" ASTNode first = getResult().getFirstChildNode();"); + writer.println(" getResult().replaceChild(first, node);"); + writer.println(" return this;"); + writer.println(" }"); + writer.println(); + writer.println( + " public PsiElement bindToElement(PsiElement element) throws " + + "IncorrectOperationException { return null; }"); + writer.println(); + writer.println(" public boolean isReferenceTo(PsiElement element) {"); + writer.println( + " return element instanceof " + head.substring(0, head.length() - 10) + + "$DeclarationImpl"); + writer.println(" && element.getTextLength() == this.getTextLength()"); + writer.println(" && element.getText().equals(this.getText());"); + writer.println(" }"); + writer.println(); + writer.println(" public Object[] getVariants() { return new Object[0]; }"); + writer.println(); + writer.println(" public boolean isSoft() { return false; }"); + } + + writer.println("}"); + writer.close(); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + } + } + } + } + + private static class InferPsiEbnfElementType implements ISymbolVisitor { + + @Override + public String visit(Align symbol) { + return symbol.getSymbol().accept(this); + } + + @Override + public String visit(Block symbol) { + String type = null; + for (Symbol sym : symbol.getSymbols()) { + String curr = sym.accept(this); + if (type != null && curr != null && !type.equals(curr)) + return "PsiElement"; + else if (type == null && curr != null) + type = curr; + } + return type; + } + + @Override + public String visit(Code symbol) { + return symbol.getSymbol().accept(this); + } + + @Override + public String visit(Error error) { + return null; + } + + @Override + public String visit(Conditional symbol) { + return symbol.getSymbol().accept(this); + } + + @Override + public String visit(IfThen symbol) { + return symbol.getThenPart().accept(this); + } + + @Override + public String visit(IfThenElse symbol) { + String thenPart = symbol.getThenPart().accept(this); + String elsePart = symbol.getElsePart().accept(this); + if (thenPart != null && elsePart != null && !thenPart.equals(elsePart)) + return "PsiElement"; + else if (thenPart == null && elsePart != null) + return elsePart; + else if (thenPart != null && (elsePart == null || thenPart.equals(elsePart))) + return thenPart; + return null; + } + + @Override + public String visit(Ignore symbol) { + return symbol.getSymbol().accept(this); + } + + @Override + public String visit(Nonterminal symbol) { + return symbol.getName(); + } + + @Override + public String visit(Offside symbol) { + return symbol.getSymbol().accept(this); + } + + @Override + public String visit(Terminal symbol) { + return null; + } + + @Override + public String visit(While symbol) { + return symbol.getBody().accept(this); + } + + @Override + public String visit(Return symbol) { + return null; + } + + @Override + public String visit(Alt symbol) { + String type = null; + for (Symbol sym : symbol.getSymbols()) { + String res = sym.accept(this); + if (type != null && res != null && !type.equals(res)) + return "PsiElement"; + else if (type == null && res != null) + type = res; + } + return type; + } + + @Override + public String visit(Opt symbol) { + return symbol.getSymbol().accept(this); + } + + @Override + public String visit(Plus symbol) { + for (Symbol sep : symbol.getSeparators()) + if (sep.accept(this) != null) + return "PsiElement"; + return symbol.getSymbol().accept(this); + } + + @Override + public String visit(Group symbol) { + String type = null; + for (Symbol sym : symbol.getSymbols()) { + String res = sym.accept(this); + if (type != null && res != null && !type.equals(res)) + return "PsiElement"; + else if (type == null && res != null) + type = res; + } + return type; + } + + @Override + public String visit(Star symbol) { + for (Symbol sep : symbol.getSeparators()) + if (sep.accept(this) != null) + return "PsiElement"; + return symbol.getSymbol().accept(this); + } + + @Override + public String visit(Start start) { + return null; +// return start.getNonterminal().accept(this); + } + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/traversal/idea/GenerateJFlex.java b/benchmarks/src/main/kotlin/org/iguana/traversal/idea/GenerateJFlex.java new file mode 100644 index 000000000..6ccf7c16c --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/traversal/idea/GenerateJFlex.java @@ -0,0 +1,434 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.traversal.idea; + +import org.iguana.grammar.condition.Condition; +import org.iguana.grammar.condition.RegularExpressionCondition; +import org.iguana.regex.Char; +import org.iguana.regex.EOF; +import org.iguana.regex.RegularExpression; +import org.iguana.regex.visitor.RegularExpressionVisitor; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.PrintWriter; +import java.io.UnsupportedEncodingException; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * Created by Anastasia Izmaylova on 17/12/15. + */ +class GenerateJFlex implements RegularExpressionVisitor { + /* + * TokenTypes.java + * .flex + * Lexer.java + */ + private final String language; + private final String path; + + private final Map regularExpressions; + + private final Set seenTokenTypes; + + private final StringBuffer header; + private final StringBuffer macros; + private final StringBuffer rules; + private final StringBuffer tokens; + + GenerateJFlex( + String language, + String path, + Map regularExpressions, + Set seenTokenTypes) { + this.language = language; + this.path = path; + this.regularExpressions = regularExpressions; + this.seenTokenTypes = seenTokenTypes; + this.header = new StringBuffer(); + this.macros = new StringBuffer(); + this.rules = new StringBuffer(); + this.tokens = new StringBuffer(); + } + + public void generate() { + + header.append("package " + language.toLowerCase() + ".gen.lexer;\n\n"); + header.append("import com.intellij.lexer.FlexLexer;\n"); + header.append("import com.intellij.psi.tree.IElementType;\n"); + header.append("import " + language.toLowerCase() + ".gen.psi." + language + "TokenTypes;\n\n"); + header.append("%%\n\n"); + header.append("%public").append("\n"); + header.append("%class " + "_" + language + "Lexer").append("\n"); + header.append("%implements FlexLexer").append("\n"); + header.append("%function advance").append("\n"); + header.append("%type IElementType").append("\n"); + header.append("%unicode").append("\n"); + header.append("%eof{").append("\n"); + header.append(" return;").append("\n"); + header.append("%eof}").append("\n\n"); + + rules.append("%%").append("\n").append("\n"); + rules.append(" {").append("\n"); + + seenTokenTypes.add("Keyword"); + tokens.append(" IElementType Keyword = new " + language + "TokenType(\"Keyword\");") + .append("\n"); + regularExpressions.entrySet().stream() + .filter(entry -> entry.getKey().startsWith("|keyword|:")) + .forEach(entry -> { + String regex = entry.getValue().accept(this); + rules.append(regex + getLookaheads(entry.getValue().getLookaheads())) + .append("\t{ return " + language + "TokenTypes.Keyword; }").append("\n"); + }); + + regularExpressions.entrySet().stream() + .filter(entry -> entry.getKey().startsWith("|regex|:")) + .forEach(entry -> { + String tokenType = entry.getKey().replaceFirst("\\|regex\\|:", "").toUpperCase(); + + if (!seenTokenTypes.contains(tokenType)) { + seenTokenTypes.add(tokenType); + tokens.append( + " IElementType " + tokenType + " = new " + language + "TokenType(\"" + + tokenType + + "\");") + .append("\n"); + } + + macros.append(tokenType + "=" + entry.getValue().accept(this)).append("\n"); + rules.append("{" + tokenType + "} " + getLookaheads(entry.getValue().getLookaheads())) + .append("\t{ return " + language + "TokenTypes." + tokenType + "; }").append("\n"); + }); + + regularExpressions.entrySet().stream() + .filter(entry -> !(entry.getKey().startsWith("|regex|:") || entry.getKey().startsWith("|keyword|:"))) + .forEach(entry -> { + String regex = entry.getValue().accept(this); + String tokenType = getTokenType(regex); + + if (!seenTokenTypes.contains(tokenType)) { + seenTokenTypes.add(tokenType); + tokens.append( + " IElementType " + tokenType + " = new " + language + "TokenType(\"" + + tokenType + + "\");") + .append("\n"); + } + + rules.append(regex + getLookaheads(entry.getValue().getLookaheads())) + .append("\t{ return " + language + "TokenTypes." + tokenType + "; }").append("\n"); + }); + + rules.append("[^]").append("\t { return " + language + "TokenTypes.BAD_CHARACTER; }\n"); + rules.append("}").append("\n"); + + seenTokenTypes.add("BAD_CHARACTER"); + + File file = new File(path + language.toLowerCase() + "/gen/psi/" + language + "TokenTypes.java"); + try { + PrintWriter writer = new PrintWriter(file.getAbsolutePath(), "UTF-8"); + writer.println("package " + language.toLowerCase() + ".gen.psi;"); + writer.println(); + writer.println("/** This file has been generated. */"); + writer.println(); + writer.println("import com.intellij.psi.tree.IElementType;"); + writer.println("import " + language.toLowerCase() + ".gen.psi." + language + "TokenType;"); + writer.println(); + writer.println("public interface " + language + "TokenTypes {"); + writer.println(); + writer.print(tokens.toString()); + if (!seenTokenTypes.contains("TERMINAL")) + writer.println(" IElementType TERMINAL = new " + language + "TokenType(\"TERMINAL\");"); + writer.println(" IElementType BAD_CHARACTER = new " + language + "TokenType(\"BAD_CHARACTER\");"); + writer.println(); + writer.println(" static IElementType get(String name) {"); + writer.println(" switch (name) {"); + for (String tokenType : seenTokenTypes) + writer.println(" case \"" + tokenType + "\": return " + tokenType + ";"); + writer.println(" default: return TERMINAL;"); + writer.println(" }"); + writer.println(" }"); + writer.println("}"); + writer.println(); + writer.close(); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + + new File(path + language.toLowerCase() + "/gen/lexer").mkdir(); + file = new File(path + language.toLowerCase() + "/gen/lexer/" + language + ".flex"); + try { + PrintWriter writer = new PrintWriter(file.getAbsolutePath(), "UTF-8"); + writer.println(header.toString()); + writer.println(macros.toString()); + writer.println(rules.toString()); + writer.println(); + writer.close(); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + + file = new File(path + language.toLowerCase() + "/gen/lexer/" + language + "Lexer.java"); + try { + PrintWriter writer = new PrintWriter(file.getAbsolutePath(), "UTF-8"); + writer.println("package " + language.toLowerCase() + ".gen.lexer;"); + writer.println(); + writer.println("/** This file has been generated. */"); + writer.println(); + writer.println("import com.intellij.lexer.FlexAdapter;"); + writer.println("import java.io.Reader;"); + writer.println(); + writer.println("public class " + language + "Lexer extends FlexAdapter {"); + writer.println(" public " + language + "Lexer() { super(new _" + language + "Lexer((Reader) null)); }"); + writer.println("}"); + writer.println(); + writer.close(); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + } + + @Override + public String visit(EOF eof) { + throw new UnsupportedOperationException(); + } + + @Override + public String visit(org.iguana.regex.Epsilon e) { + return ""; + } + + @Override + public String visit(Char c) { + return getChar(c.getValue()); + } + + @Override + public String visit(org.iguana.regex.CharRange r) { + return "[" + getRange(r) + "]"; + } + + @Override + public String visit(org.iguana.regex.Star s) { + return s.getSymbol().accept(this) + "*"; + } + + @Override + public String visit(org.iguana.regex.Plus p) { + return p.getSymbol().accept(this) + "+"; + } + + @Override + public String visit(org.iguana.regex.Opt o) { + return o.getSymbol().accept(this) + "?"; + } + + @Override + public String visit(org.iguana.regex.Alt symbol) { + Map> partition = symbol.getSymbols().stream().collect( + Collectors.partitioningBy(s -> isCharClass(s))); + List charClasses = partition.get(true); + List other = partition.get(false); + + StringBuilder sb = new StringBuilder(); + + if (!charClasses.isEmpty() && !other.isEmpty()) { + int left = charClasses.size(); + int right = other.stream().map(s -> (RegularExpression) s).mapToInt(r -> r.length()).max().getAsInt(); + + sb.append("("); + if (left > right) { + sb.append("[" + charClasses.stream().map(s -> asCharClass(s)).collect(Collectors.joining()) + "]"); + sb.append("|"); + sb.append(other.stream().map(s -> s.accept(this)).collect(Collectors.joining("|"))); + } else { + sb.append(other.stream().sorted(RegularExpression.lengthComparator()).map(s -> s.accept(this)) + .collect(Collectors.joining("|"))); + sb.append("|"); + sb.append("[" + charClasses.stream().map(s -> asCharClass(s)).collect(Collectors.joining()) + "]"); + } + sb.append(")"); + } else if (!charClasses.isEmpty()) { + sb.append("[" + charClasses.stream().map(s -> asCharClass(s)).collect(Collectors.joining()) + "]"); + } else { + sb.append("(" + other.stream().sorted(RegularExpression.lengthComparator()).map(s -> s.accept(this)) + .collect(Collectors.joining("|")) + ")"); + } + + return sb.toString(); + } + + @Override + public String visit(org.iguana.regex.Seq symbol) { + + List symbols = symbol.getSymbols(); + + if (symbols.size() == 1) + return "[" + symbols.get(0).accept(this) + "]"; + + return "(" + symbols.stream().map(s -> s.accept(this)).collect(Collectors.joining()) + ")"; + } + + @Override + public String visit(org.iguana.regex.Reference reference) { + throw new RuntimeException(); + } + + private boolean isCharClass(RegularExpression s) { + if (!s.getLookaheads().isEmpty()) return false; + return s instanceof Char || s instanceof org.iguana.regex.CharRange; + } + + private String asCharClass(RegularExpression s) { + if (s instanceof Char) { + Char c = (Char) s; + return getChar(c.getValue()); + } else if (s instanceof org.iguana.regex.CharRange) { + org.iguana.regex.CharRange r = (org.iguana.regex.CharRange) s; + return getRange(r); + } + + throw new RuntimeException(s + " is not a character or character class."); + } + + private String getTokenType(String terminal) { + switch (terminal) { + case "[\\(]": + return "OPEN_PARENTHESIS"; + case "[\\)]": + return "CLOSE_PARENTHESIS"; + case "[\\[]": + return "OPEN_BRACKET"; + case "[\\]]": + return "CLOSE_BRACKET"; + case "[\\{]": + return "OPEN_BRACE"; + case "[\\}]": + return "CLOSE_BRACE"; + case "\\*]": + return "OPERATOR"; + case "[/]": + return "OPERATOR"; + case "[\\+]": + return "OPERATOR"; + case "[\\-]": + return "OPERATOR"; + case "[&]": + return "OPERATOR"; + case "[\\|]": + return "OPERATOR"; + case "[=]": + return "OPERATOR"; + default: + return "TERMINAL"; + } + } + + private String getConditions(Set conditions) { + StringBuffer code = new StringBuffer(); + for (Condition condition : conditions) + code.append(getCondition(condition)); + + return code.toString(); + } + + private String getLookaheads(Set lookaheads) { + return ""; + } + + private String getLookbehinds(Set lookbehinds) { + return ""; + } + + private String getCondition(Condition condition) { + switch (condition.getType()) { + case NOT_FOLLOW: + return "/!(" + getRegularExpression(condition).accept(this) + ")"; + case FOLLOW: + return "/(" + getRegularExpression(condition).accept(this) + ")"; + default: + return ""; + } + } + + private static RegularExpression getRegularExpression(Condition condition) { + if (condition instanceof RegularExpressionCondition) { + return ((RegularExpressionCondition) condition).getRegularExpression(); + } + throw new UnsupportedOperationException(); + } + + private String getChar(int c) { + if (org.iguana.regex.CharacterRanges.isPrintableAscii(c)) + return escape((char) c + ""); + else + return escape(String.format("\\u%04X", c)); + } + + private String getRange(org.iguana.regex.CharRange r) { + return getChar(r.getStart()) + "-" + getChar(r.getEnd()); + } + + private String escape(String s) { + String backslash = "\\"; + switch (s) { + case "(": + case ")": + case "[": + case "]": + case "{": + case "}": + case "\\": + case "\"": + case "^": + case "-": + case "=": + case "$": + case "!": + case "|": + case "?": + case "*": + case "+": + case ".": + return backslash + s; + default: + return s; + } + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/traversal/idea/GenerateParser.java b/benchmarks/src/main/kotlin/org/iguana/traversal/idea/GenerateParser.java new file mode 100644 index 000000000..21223ecf6 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/traversal/idea/GenerateParser.java @@ -0,0 +1,210 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.traversal.idea; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.PrintWriter; +import java.io.UnsupportedEncodingException; + +/** + * Created by Anastasia Izmaylova on 18/12/15. + */ +public class GenerateParser { + /* + * Parser.java + * ParserDefinition.java + * File.java + */ + public static void generate(String language, String path) { + new File(path + language.toLowerCase() + "/gen/parser").mkdir(); + File file = new File(path + language.toLowerCase() + "/gen/parser/" + language + "Parser.java"); + try { + PrintWriter writer = new PrintWriter(file.getAbsolutePath(), "UTF-8"); + writer.println("package " + language.toLowerCase() + ".gen.parser;"); + writer.println(); + writer.println("/* This file has been generated. */"); + writer.println(); + writer.println("import com.intellij.lang.ASTNode;"); + writer.println("import com.intellij.lang.PsiBuilder;"); + writer.println("import com.intellij.lang.PsiParser;"); + writer.println("import com.intellij.psi.impl.source.tree.Factory;"); + writer.println("import com.intellij.psi.tree.IElementType;"); + writer.println("import iguana.parsetrees.sppf.NonterminalNode;"); + writer.println("import iguana.parsetrees.term.SPPFToTerms;"); + writer.println("import iguana.utils.input.Input;"); + writer.println("import org.iguana.grammar.Grammar;"); + writer.println("import org.iguana.grammar.GrammarGraph;"); + writer.println("import org.iguana.grammar.symbol.Nonterminal;"); + writer.println("import org.iguana.grammar.symbol.Start;"); + writer.println("import org.iguana.grammar.transformation.DesugarPrecedenceAndAssociativity;"); + writer.println("import org.iguana.grammar.transformation.EBNFToBNF;"); + writer.println("import org.iguana.grammar.transformation.LayoutWeaver;"); + writer.println("import org.iguana.parser.IguanaParser;"); + writer.println("import org.iguana.parser.ParseResult;"); + writer.println("import org.iguana.traversal.idea.Names;"); + writer.println("import org.iguana.util.Configuration;"); + writer.println(); + writer.println("public class " + language + "Parser implements PsiParser {"); + writer.println(); + writer.println(" private Grammar grammar;"); + writer.println(" private Start start;"); + writer.println(" private GrammarGraph graph;"); + writer.println(); + writer.println(" public ASTNode getParserTree(IElementType root, PsiBuilder builder) {"); + writer.println(" Input input = Input.fromString(builder.getOriginalText().toString());"); + writer.println(" if (graph == null) {"); + writer.println( + " grammar = Grammar.load(this.getClass().getClassLoader().getResourceAsStream(\"" + + language.toLowerCase() + "/gen/parser/grammar/" + language + "\"));"); + writer.println( + " DesugarPrecedenceAndAssociativity precedenceAndAssociativity = " + + "new DesugarPrecedenceAndAssociativity();"); + writer.println(" precedenceAndAssociativity.setOP2();"); + writer.println(" grammar = new EBNFToBNF().transform(grammar);"); + writer.println(" grammar = precedenceAndAssociativity.transform(grammar);"); + writer.println(" grammar = new Names().transform(grammar);"); + writer.println(" grammar = new LayoutWeaver().transform(grammar);"); + writer.println(" start = grammar.getStartSymbol(Nonterminal.withName(\"\"));"); + writer.println(" graph = GrammarGraph.from(grammar, input, Configuration.DEFAULT);"); + writer.println(" }"); + writer.println(" ParseResult result = IguanaParser.getParserTree(input, graph, start);"); + writer.println(" if (result.isParseSuccess()) {"); + writer.println(" System.out.println(\"Success...\");"); + writer.println(" NonterminalNode sppf = result.asParseSuccess().getResult();"); + writer.println( + " ASTNode ast = SPPFToTerms.convertNoSharing(sppf, new " + language + "TermBuilder());"); + writer.println(" return ast;"); + writer.println(" } else {"); + writer.println(" System.out.println(\"Parse error...\");"); + writer.println( + " return Factory.createErrorElement(\"Sorry, you have a getParserTree error.\");"); + writer.println(" }"); + writer.println(" }"); + writer.println("}"); + writer.close(); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + + file = new File(path + language.toLowerCase() + "/gen/parser/" + language + "ParserDefinition.java"); + try { + PrintWriter writer = new PrintWriter(file.getAbsolutePath(), "UTF-8"); + writer.println("package " + language.toLowerCase() + ".gen.parser;"); + writer.println(); + writer.println("/* This file has been generated. */"); + writer.println(); + writer.println("import com.intellij.lang.ASTNode;"); + writer.println("import com.intellij.lang.Language;"); + writer.println("import com.intellij.lang.ParserDefinition;"); + writer.println("import com.intellij.lang.PsiParser;"); + writer.println("import com.intellij.lexer.Lexer;"); + writer.println("import com.intellij.openapi.project.Project;"); + writer.println("import com.intellij.psi.FileViewProvider;"); + writer.println("import com.intellij.psi.PsiElement;"); + writer.println("import com.intellij.psi.PsiFile;"); + writer.println("import com.intellij.psi.text.BlockSupport;"); + writer.println("import com.intellij.psi.tree.IFileElementType;"); + writer.println("import com.intellij.psi.tree.TokenSet;"); + writer.println("import " + language.toLowerCase() + ".gen.lang." + language + "File;"); + writer.println("import " + language.toLowerCase() + ".gen.lang." + language + "Lang;"); + writer.println("import " + language.toLowerCase() + ".gen.lexer." + language + "Lexer;"); + writer.println("import " + language.toLowerCase() + ".gen.psi." + language + "ElementTypes;"); + writer.println("import " + language.toLowerCase() + ".gen.psi." + language + "TokenTypes;"); + writer.println(); + writer.println("public class " + language + "ParserDefinition implements ParserDefinition {"); + writer.println(); + writer.println( + " public static final IFileElementType FILE = new IFileElementType(Language.<" + language + + "Lang>findInstance(" + language + "Lang.class));"); + writer.println(); + writer.println(" public Lexer createLexer(Project project) { return new " + language + "Lexer(); }"); + writer.println(); + writer.println( + " public PsiParser createParser(Project project) { return new " + language + "Parser(); }"); + writer.println(); + writer.println(" public IFileElementType getFileNodeType() { return FILE; }"); + writer.println(); + writer.println(" public TokenSet getWhitespaceTokens() { return TokenSet.EMPTY; }"); + writer.println(); + writer.println(" public TokenSet getCommentTokens() { return TokenSet.EMPTY; }"); + writer.println(); + writer.println(" public TokenSet getStringLiteralElements() { return TokenSet.EMPTY; }"); + writer.println(); + writer.println(" public PsiElement createElement(ASTNode node) { return " + language + + "ElementTypes.Factory.createElement(node); }"); + writer.println(); + writer.println(" public PsiFile createFile(FileViewProvider viewProvider) {"); + writer.println(" " + language + "File file = new " + language + "File(viewProvider);"); + writer.println(" file.putUserData(BlockSupport.DO_NOT_REPARSE_INCREMENTALLY, Boolean.TRUE);"); + writer.println(" return file;"); + writer.println(" }"); + writer.println(); + writer.println( + " public SpaceRequirements spaceExistanceTypeBetweenTokens(ASTNode left, ASTNode right) " + + "{ return SpaceRequirements.MAY; }"); + writer.println("}"); + writer.close(); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + + file = new File(path + language.toLowerCase() + "/gen/lang/" + language + "File.java"); + try { + PrintWriter writer = new PrintWriter(file.getAbsolutePath(), "UTF-8"); + writer.println("package " + language.toLowerCase() + ".gen.lang;"); + writer.println(); + writer.println("/* This file has been generated. */"); + writer.println(); + writer.println("import com.intellij.extapi.psi.PsiFileBase;"); + writer.println("import com.intellij.openapi.fileTypes.FileType;"); + writer.println("import com.intellij.psi.FileViewProvider;"); + writer.println("import javax.swing.Icon;"); + writer.println(); + writer.println("public class " + language + "File extends PsiFileBase {"); + writer.println( + " public " + language + "File(FileViewProvider viewProvider) { super(viewProvider, " + language + + "Lang.instance); }"); + writer.println(" public FileType getFileType() { return " + language + "FileType.instance; }"); + writer.println(" public String toString() { return \"" + language + " file\"; }"); + writer.println(" public Icon getIcon(int flags) { return super.getIcon(flags); }"); + writer.println("}"); + writer.close(); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + + // generateTreeBuider(language, path); + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/traversal/idea/GenerateTermBuilder.java b/benchmarks/src/main/kotlin/org/iguana/traversal/idea/GenerateTermBuilder.java new file mode 100644 index 000000000..f60639e55 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/traversal/idea/GenerateTermBuilder.java @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.traversal.idea; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.PrintWriter; +import java.io.UnsupportedEncodingException; + +/** + * Created by Anastasia Izmaylova on 18/12/15. + */ +public class GenerateTermBuilder { + /* + * TermBuilder.java + */ + // CHECKSTYLE:OFF OperatorWrap + public static void generate(String language, String path) { + File file = new File(path + language.toLowerCase() + "/gen/parser/" + language + "TermBuilder.java"); + try { + PrintWriter writer = new PrintWriter(file.getAbsolutePath(), "UTF-8"); + writer.println("package " + language.toLowerCase() + ".gen.parser;"); + writer.println(); + writer.println("/* This file has been generated. */"); + writer.println(); + writer.println("import com.intellij.lang.ASTFactory;"); + writer.println("import com.intellij.psi.impl.source.tree.CompositeElement;"); + writer.println("import com.intellij.psi.impl.source.tree.TreeElement;"); + writer.println("import com.intellij.psi.tree.IElementType;"); + writer.println("import " + language.toLowerCase() + ".gen.psi." + language + "ElementTypes;"); + writer.println("import " + language.toLowerCase() + ".gen.psi." + language + "TokenTypes;"); + writer.println("import iguana.parsetrees.slot.TerminalNodeType;"); + writer.println("import iguana.parsetrees.term.RuleType;"); + writer.println("import iguana.parsetrees.term.TermBuilder;"); + writer.println("import iguana.parsetrees.term.TerminalType;"); + writer.println("import iguana.utils.input.Input;"); + writer.println("import org.iguana.grammar.symbol.Rule;"); + writer.println("import scala.collection.*;"); + writer.println(); + writer.println("public class " + language + "TermBuilder implements TermBuilder {"); + writer.println(); + writer.println(" @Override"); + writer.println(" public TreeElement terminalTerm(TerminalType type, int l, int r, Input input) {"); + writer.println(" if (type.nodeType() == TerminalNodeType.Regex()) {"); + writer.println(" IElementType tokenType = IGGYTokenTypes.get(type.name().toUpperCase());"); + writer.println(" return ASTFactory.leaf(tokenType, input.subString(l, r));"); + writer.println(" }"); + writer.println(" return ASTFactory.leaf(IGGYTokenTypes.TERMINAL, input.subString(l, r));"); + writer.println(" }"); + writer.println(); + writer.println(" @Override"); + writer.println( + " public TreeElement nonterminalTerm(RuleType type, Seq children, int l, int r," + + " Input input) {"); + writer.println(" Rule rule = (Rule) type;"); + writer.println( + " String name = rule.getHead().getName().toUpperCase() + (rule.getLabel() == " + + "null? \"\" : \"_\" + " + + "rule.getLabel().toUpperCase());"); + writer.println( + " CompositeElement node = ASTFactory.composite(" + language + "ElementTypes.get(name));"); + writer.println(" Iterator iterator = children.iterator();"); + writer.println( + " while (iterator.hasNext()) node.rawAddChildrenWithoutNotifications(iterator.next());"); + writer.println(" return node;"); + writer.println(" }"); + writer.println(); + writer.println(" @Override"); + writer.println( + " public TreeElement ambiguityTerm(scala.collection.Seq> " + + "children) { throw new RuntimeException(\"Not yet supported in the idea tree builder" + + ": ambiguity.\"); }"); + writer.println(); + writer.println(" @Override"); + writer.println( + " public TreeElement cycle(String label) { throw new RuntimeException(\"Not yet supported in " + + "the idea tree builder: cycles.\"); }"); + writer.println(); + writer.println(" @Override"); + writer.println(" public TreeElement star(Seq children) {"); + writer.println(" CompositeElement node = ASTFactory.composite(" + language + "ElementTypes.LIST);"); + writer.println(" Iterator iterator = children.iterator();"); + writer.println( + " while (iterator.hasNext()) node.rawAddChildrenWithoutNotifications(iterator.next());"); + writer.println(" return node;"); + writer.println(" }"); + writer.println(); + writer.println(" @Override"); + writer.println(" public TreeElement plus(Seq children) { return star(children); }"); + writer.println(); + writer.println(" @Override"); + writer.println(" public TreeElement opt(TreeElement first) {"); + writer.println(" CompositeElement node = ASTFactory.composite(" + language + "ElementTypes.OPT);"); + writer.println(" node.rawAddChildrenWithoutNotifications(first);"); + writer.println(" return node;"); + writer.println(" }"); + writer.println(); + writer.println(" @Override"); + writer.println(" public TreeElement group(Seq children) {"); + writer.println(" CompositeElement node = ASTFactory.composite(" + language + "ElementTypes.SEQ);"); + writer.println(" Iterator iterator = children.iterator();"); + writer.println( + " while (iterator.hasNext()) node.rawAddChildrenWithoutNotifications(iterator.next());"); + writer.println(" return node;"); + writer.println(" }"); + writer.println(); + writer.println(" @Override"); + writer.println(" public TreeElement alt(Seq children) {"); + writer.println(" CompositeElement node = ASTFactory.composite(" + language + "ElementTypes.ALT);"); + writer.println(" Iterator iterator = children.iterator();"); + writer.println( + " while (iterator.hasNext()) node.rawAddChildrenWithoutNotifications(iterator.next());"); + writer.println(" return node;"); + writer.println(" }"); + writer.println(); + writer.println(" @Override"); + writer.println(" public TreeElement epsilon(int i) { return ASTFactory.leaf(" + language + + "TokenTypes.TERMINAL, \"\"); }"); + writer.println("}"); + writer.close(); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + } + // CHECKSTYLE:ON OperatorWrap +} diff --git a/benchmarks/src/main/kotlin/org/iguana/traversal/idea/IdeaIDEGenerator.java b/benchmarks/src/main/kotlin/org/iguana/traversal/idea/IdeaIDEGenerator.java new file mode 100644 index 000000000..f9ddc9277 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/traversal/idea/IdeaIDEGenerator.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.traversal.idea; + +import org.iguana.grammar.runtime.RuntimeGrammar; +import org.iguana.regex.RegularExpression; + +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Set; + +/** + * Created by Anastasia Izmaylova on 28/09/15. + */ + +public class IdeaIDEGenerator { + + public void generate(RuntimeGrammar grammar, String language, String extension, String path) { + + path = path.endsWith("/") ? path : path + "/"; + + Set tokenTypes = new HashSet<>(); + Map terminals = new LinkedHashMap<>(); + + GenerateBasicFiles.generate(language, extension, path); + new CollectRegularExpressions(terminals).collect(grammar); + new GenerateJFlex(language, path, terminals, tokenTypes).generate(); + GenerateBasicHighlighter.generate(language, path, tokenTypes); + GenerateElements.generate(grammar.getRules(), language, path); + GenerateParser.generate(language, path); + GenerateTermBuilder.generate(language, path); + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/traversal/idea/Names.java b/benchmarks/src/main/kotlin/org/iguana/traversal/idea/Names.java new file mode 100644 index 000000000..90a0edb80 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/traversal/idea/Names.java @@ -0,0 +1,247 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.traversal.idea; + +import org.iguana.grammar.runtime.PrecedenceLevel; +import org.iguana.grammar.runtime.Recursion; +import org.iguana.grammar.runtime.RuntimeGrammar; +import org.iguana.grammar.runtime.RuntimeRule; +import org.iguana.grammar.symbol.Align; +import org.iguana.grammar.symbol.Alt; +import org.iguana.grammar.symbol.Associativity; +import org.iguana.grammar.symbol.Block; +import org.iguana.grammar.symbol.Code; +import org.iguana.grammar.symbol.Conditional; +import org.iguana.grammar.symbol.Error; +import org.iguana.grammar.symbol.Group; +import org.iguana.grammar.symbol.IfThen; +import org.iguana.grammar.symbol.IfThenElse; +import org.iguana.grammar.symbol.Ignore; +import org.iguana.grammar.symbol.LayoutStrategy; +import org.iguana.grammar.symbol.Nonterminal; +import org.iguana.grammar.symbol.Offside; +import org.iguana.grammar.symbol.Opt; +import org.iguana.grammar.symbol.Plus; +import org.iguana.grammar.symbol.Return; +import org.iguana.grammar.symbol.Star; +import org.iguana.grammar.symbol.Start; +import org.iguana.grammar.symbol.Symbol; +import org.iguana.grammar.symbol.SymbolBuilder; +import org.iguana.grammar.symbol.Terminal; +import org.iguana.grammar.symbol.While; +import org.iguana.grammar.transformation.GrammarTransformation; +import org.iguana.traversal.ISymbolVisitor; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * Created by Anastasia Izmaylova on 20/10/15. + */ + +// @0 => $Declaration ::= Name +// @1 => $Reference ::= Name +public class Names implements GrammarTransformation { + + @Override + public RuntimeGrammar transform(RuntimeGrammar grammar) { + List rules = new ArrayList<>(); + + NameVisitor visitor = new NameVisitor(rules, (Nonterminal) grammar.getLayout()); + + for (RuntimeRule rule : grammar.getRules()) + rules.add(visitor.visitRule(rule)); + + return RuntimeGrammar.builder() + .addRules(rules) + .addEBNFl(grammar.getEBNFLefts()) + .addEBNFr(grammar.getEBNFRights()) + .setLayout(grammar.getLayout()) + .build(); + } + + private static class NameVisitor implements ISymbolVisitor { + + final List rules; + private Set heads = new HashSet<>(); + + private final Nonterminal layout; + + NameVisitor(List rules, Nonterminal layout) { + this.rules = rules; + this.layout = layout; + } + + public RuntimeRule visitRule(RuntimeRule rule) { + RuntimeRule.Builder builder = rule.copy(); + List symbols = new ArrayList<>(); + for (Symbol symbol : rule.getBody()) + symbols.add(visitSymbol(symbol)); + return builder.setSymbols(symbols).build(); + } + + public Symbol visitSymbol(Symbol symbol) { + SymbolBuilder builder = symbol.accept(this).copy(); + builder.addPreConditions(symbol.getPreConditions()); + builder.addPostConditions(symbol.getPostConditions()); + builder.setLabel(symbol.getLabel()); + return builder.build(); + } + + @Override + public Symbol visit(Align symbol) { + return Align.align(visitSymbol(symbol.getSymbol())); + } + + @Override + public Symbol visit(Block block) { + return Block.block(block.getSymbols().stream().map(this::visitSymbol).toArray(Symbol[]::new)); + } + + @Override + public Symbol visit(Code symbol) { + return Code.code(visitSymbol(symbol.getSymbol()), symbol.getStatements()); + } + + @Override + public Symbol visit(Error error) { + return error; + } + + @Override + public Symbol visit(Conditional symbol) { + return Conditional.when(visitSymbol(symbol.getSymbol()), symbol.getExpression()); + } + + @Override + public Symbol visit(IfThen symbol) { + return IfThen.ifThen(symbol.getExpression(), visitSymbol(symbol.getThenPart())); + } + + @Override + public Symbol visit(IfThenElse symbol) { + return IfThenElse.ifThenElse(symbol.getExpression(), visitSymbol(symbol.getThenPart()), + visitSymbol(symbol.getElsePart())); + } + + @Override + public Symbol visit(Ignore symbol) { + return Ignore.ignore(visitSymbol(symbol.getSymbol())); + } + + @Override + public Symbol visit(Nonterminal symbol) { + Map attributes = symbol.getAttributes(); + if (attributes.containsKey("name")) { + int value = (Integer) attributes.get("name"); + Nonterminal sym = symbol; + switch (value) { + case 0: + sym = Nonterminal.withName(symbol.getName() + "$Declaration"); + if (!heads.contains(sym)) { + heads.add(sym); + rules.add(RuntimeRule.withHead(sym).addSymbol(symbol) + .setLayout(layout).setLayoutStrategy(LayoutStrategy.INHERITED) + .setRecursion(Recursion.NON_REC).setAssociativity(Associativity.UNDEFINED) + .setPrecedence(-1).setPrecedenceLevel(PrecedenceLevel.getFirstAndDone()) + .build()); + } + return sym; + case 1: + sym = Nonterminal.withName(symbol.getName() + "$Reference"); + if (!heads.contains(sym)) { + heads.add(sym); + rules.add(RuntimeRule.withHead(sym).addSymbol(symbol) + .setLayout(layout).setLayoutStrategy(LayoutStrategy.INHERITED) + .setRecursion(Recursion.NON_REC).setAssociativity(Associativity.UNDEFINED) + .setPrecedence(-1).setPrecedenceLevel(PrecedenceLevel.getFirstAndDone()) + .build()); + } + return sym; + default: + break; + } + } + return symbol; + } + + @Override + public Symbol visit(Offside symbol) { + return Offside.offside(visitSymbol(symbol.getSymbol())); + } + + @Override + public Symbol visit(Terminal symbol) { + return symbol; + } + + @Override + public Symbol visit(While symbol) { + return While.whileLoop(symbol.getExpression(), visitSymbol(symbol.getBody())); + } + + @Override + public Symbol visit(Return symbol) { + return symbol; + } + + @Override + public Symbol visit(Alt symbol) { + return Alt.from(symbol.getSymbols().stream().map(s -> visitSymbol(s)).toArray(Symbol[]::new)); + } + + @Override + public Symbol visit(Opt symbol) { + return Opt.from(visitSymbol(symbol.getSymbol())); + } + + @Override + public Symbol visit(Plus symbol) { + return new Plus.Builder(visitSymbol(symbol.getSymbol())).addSeparators(symbol.getSeparators()).build(); + } + + @Override + public Symbol visit(Group symbol) { + return Group.from(symbol.getSymbols().stream().map(s -> visitSymbol(s)).toArray(Symbol[]::new)); + } + + @Override + public Symbol visit(Star symbol) { + return new Star.Builder(visitSymbol(symbol.getSymbol())).addSeparators(symbol.getSeparators()).build(); + } + + @Override + public Symbol visit(Start start) { + return start; +// return Start.from((Nonterminal) start.getNonterminal().accept(this)); + } + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/util/Configuration.java b/benchmarks/src/main/kotlin/org/iguana/util/Configuration.java new file mode 100644 index 000000000..fa05602b9 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/util/Configuration.java @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.util; + +import org.iguana.util.config.XMLConfigFileParser; +import org.iguana.utils.logging.LogLevel; + +public class Configuration { + + public static final int DEFAULT_LOOKAHEAD = 1; + + private final LookupImpl gssLookupImpl; + + private final MatcherType matcherType; + + private final int lookAheadCount; + + private final HashMapImpl hashmapImpl; + + private final EnvironmentImpl envImpl; + + private final LogLevel logLevel; + + private Configuration(Builder builder) { + this.gssLookupImpl = builder.gssLookupImpl; + this.lookAheadCount = builder.lookaheadCount; + this.matcherType = builder.matcherType; + this.hashmapImpl = builder.hashmapImpl; + this.envImpl = builder.envImpl; + this.logLevel = builder.logLevel; + } + + public static Configuration load() { + Configuration configuration; + try { + XMLConfigFileParser parser = XMLConfigFileParser.create("config.xml"); + configuration = parser.getConfiguration(); + } catch (Exception e) { + throw new RuntimeException(e); + } + return configuration; + } + + public LookupImpl getGSSLookupImpl() { + return gssLookupImpl; + } + + public int getLookAheadCount() { + return lookAheadCount; + } + + public MatcherType getMatcherType() { + return matcherType; + } + + public HashMapImpl getHashmapImpl() { + return hashmapImpl; + } + + public EnvironmentImpl getEnvImpl() { + return envImpl; + } + + public LogLevel getLogLevel() { + return logLevel; + } + + public static Builder builder() { + return new Builder(); + } + + public enum MatcherType { + DFA, + JAVA_REGEX + } + + public enum LookupImpl { + ARRAY, + HASH_MAP + } + + public enum HashMapImpl { + JAVA, + INT_OPEN_ADDRESSING + } + + public enum EnvironmentImpl { + ARRAY, + INT_ARRAY, + HASH_MAP, + TRIE + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + + sb.append(MatcherType.class.getName()).append(": ").append(matcherType) + .append(LookupImpl.class.getName()).append(": ").append(gssLookupImpl) + .append(HashMapImpl.class.getName()).append(": ").append(hashmapImpl) + .append("LookaheadCount").append(": ").append(lookAheadCount); + + return sb.toString(); + } + + public static class Builder { + + private LookupImpl gssLookupImpl = LookupImpl.HASH_MAP; + private MatcherType matcherType = MatcherType.JAVA_REGEX; + private HashMapImpl hashmapImpl = HashMapImpl.JAVA; // HashMapImpl.INT_OPEN_ADDRESSING; + private int lookaheadCount = DEFAULT_LOOKAHEAD; + private LogLevel logLevel = LogLevel.NONE; + + private EnvironmentImpl envImpl = EnvironmentImpl.TRIE; + + public Configuration build() { + return new Configuration(this); + } + + public Builder setGSSLookupImpl(LookupImpl gssLookupImpl) { + this.gssLookupImpl = gssLookupImpl; + return this; + } + + public Builder setMatcherType(MatcherType matcherType) { + this.matcherType = matcherType; + return this; + } + + public Builder setHashmapImpl(HashMapImpl hashmapImpl) { + this.hashmapImpl = hashmapImpl; + return this; + } + + public Builder setLookaheadCount(int lookaheadCount) { + this.lookaheadCount = lookaheadCount; + return this; + } + + public Builder setEnvironmentImpl(EnvironmentImpl impl) { + this.envImpl = impl; + return this; + } + + public Builder setLogLevel(LogLevel logLevel) { + this.logLevel = logLevel; + return this; + } + + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/util/Holder.java b/benchmarks/src/main/kotlin/org/iguana/util/Holder.java new file mode 100644 index 000000000..2305a3636 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/util/Holder.java @@ -0,0 +1,12 @@ +package org.iguana.util; + +public class Holder { + + private T val; + + public void set(T val) { this.val = val; } + + public T get() { return val; } + + public boolean has() { return val != null; } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/util/ParserLogger.java b/benchmarks/src/main/kotlin/org/iguana/util/ParserLogger.java new file mode 100644 index 000000000..fba6fb330 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/util/ParserLogger.java @@ -0,0 +1,174 @@ +package org.iguana.util; + +import org.iguana.grammar.slot.GrammarSlot; +import org.iguana.gss.GSSEdge; +import org.iguana.gss.GSSNode; +import org.iguana.parser.ParseError; +import org.iguana.parser.descriptor.Descriptor; +import org.iguana.result.Result; +import org.iguana.sppf.IntermediateNode; +import org.iguana.sppf.NonPackedNode; +import org.iguana.sppf.NonterminalNode; +import org.iguana.sppf.PackedNode; +import org.iguana.sppf.TerminalNode; +import org.iguana.utils.logging.IguanaLogger; +import org.iguana.utils.logging.JavaUtilIguanaLogger; +import org.iguana.utils.logging.LogLevel; + +import java.util.Arrays; + +public class ParserLogger { + + private static final ParserLogger instance = new ParserLogger(); + + private int descriptorsCount; + + private int countNonterminalNodes; + + private int countIntermediateNodes; + + private int countTerminalNodes; + + private int countPackedNodes; + + private int countAmbiguousNodes; + + private int countGSSNodes; + + private int countGSSEdges; + + private final IguanaLogger logger; + + private boolean logEnabled = false; + + public ParserLogger() { + Configuration config = Configuration.load(); + if (config.getLogLevel() == LogLevel.NONE) + logger = IguanaLogger.DEFAULT; + else + logger = new JavaUtilIguanaLogger("IguanaParser Logger", config.getLogLevel()); + } + + public static ParserLogger getInstance() { + return instance; + } + + public void reset() { + descriptorsCount = 0; + countNonterminalNodes = 0; + countIntermediateNodes = 0; + countTerminalNodes = 0; + countPackedNodes = 0; + countAmbiguousNodes = 0; + countGSSNodes = 0; + countGSSEdges = 0; + } + + public void enable() { + logEnabled = true; + } + + public void terminalNodeAdded(TerminalNode node) { + countTerminalNodes++; + if (logEnabled) logger.log("Terminal node added %s", node); + } + + public void nonterminalNodeAdded(NonterminalNode node) { + countNonterminalNodes++; + if (logEnabled) logger.log("Nonterminal node added %s", node); + } + + public void intermediateNodeAdded(IntermediateNode node) { + countIntermediateNodes++; + if (logEnabled) logger.log("Intermediate node added %s", node); + } + + public void packedNodeAdded(PackedNode packedNode) { + countPackedNodes++; + if (logEnabled) logger.log("Packed node added %s", packedNode); + } + + public void ambiguousNodeAdded(NonPackedNode node) { + countAmbiguousNodes++; + if (logEnabled) logger.log("Ambiguous node added: %s", node); + } + + public void gssNodeAdded(GSSNode node, Object[] data) { + countGSSNodes++; + if (logEnabled) { + if (data != null) { + logger.log("GSS node added %s(%s)", node, Arrays.deepToString(data)); + } else { + logger.log("GSS node added %s", node, Arrays.deepToString(data)); + } + } + } + + public void gssEdgeAdded(GSSEdge edge) { + countGSSEdges++; + if (logEnabled) logger.log("GSS Edge added %s", edge); + } + + public void descriptorAdded(Descriptor descriptor) { + descriptorsCount++; + if (logEnabled) + logger.log("Descriptor created: %s", descriptor); + } + + public void pop(GSSNode gssNode, int inputIndex, T child, Object value) { + if (logEnabled) logger.log("Pop %s, %d, %s, %s", gssNode, inputIndex, child, value); + } + + public void error(ParseError error) { + if (logEnabled) { + logger.log("Error recorded at %s %d %s", error.getGrammarSlot(), error.getInputIndex(), + error.getDescription()); + } + } + + public void processDescriptor(Descriptor descriptor) { + if (logEnabled) logger.log("Processing %s", descriptor); + } + + public void logFollowTestFailed(GrammarSlot grammarSlot, int inputIndex, int c, String expected) { + if (logEnabled) { + logger.log("Test follow failed at %s at input index %d. Found %d but expected %s", + grammarSlot, + inputIndex, + c, expected); + } + } + + public int getDescriptorsCount() { + return descriptorsCount; + } + + public int getCountNonterminalNodes() { + return countNonterminalNodes; + } + + public int getCountIntermediateNodes() { + return countIntermediateNodes; + } + + public int getCountTerminalNodes() { + return countTerminalNodes; + } + + public int getCountPackedNodes() { + return countPackedNodes; + } + + public int getCountAmbiguousNodes() { + return countAmbiguousNodes; + } + + public int getCountGSSNodes() { + return countGSSNodes; + } + + public int getCountGSSEdges() { + return countGSSEdges; + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/util/RunningTime.java b/benchmarks/src/main/kotlin/org/iguana/util/RunningTime.java new file mode 100644 index 000000000..906328445 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/util/RunningTime.java @@ -0,0 +1,31 @@ +package org.iguana.util; + +public class RunningTime { + + private final long nanoTime; + private final long userTime; + private final long systemTime; + + public RunningTime(long nanoTime, long userTime, long systemTime) { + this.nanoTime = nanoTime; + this.userTime = userTime; + this.systemTime = systemTime; + } + + public long getNanoTime() { + return nanoTime; + } + + public long getUserTime() { + return userTime; + } + + public long getSystemTime() { + return systemTime; + } + + @Override + public String toString() { + return String.format("(Nano Time: %d, User Time: %d, System Time: %d)", nanoTime, userTime, systemTime); + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/util/Tuple.java b/benchmarks/src/main/kotlin/org/iguana/util/Tuple.java new file mode 100644 index 000000000..527f99fe7 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/util/Tuple.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.util; + +import java.util.Objects; + +public class Tuple { + + protected T t; + protected K k; + + public Tuple(T t, K k) { + Objects.requireNonNull(t); + Objects.requireNonNull(k); + this.t = t; + this.k = k; + } + + public T getFirst() { + return t; + } + + public K getSecond() { + return k; + } + + public static Tuple of(T t, K k) { + return new Tuple<>(t, k); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (!(obj instanceof Tuple)) return false; + @SuppressWarnings("unchecked") + Tuple other = (Tuple) obj; + + return t.equals(other.t) && k.equals(other.k); + } + + @Override + public int hashCode() { + return (t == null ? 0 : t.hashCode()) + (k == null ? 0 : k.hashCode()); + } + + @Override + public String toString() { + return String.format("(%s, %s)", t, k); + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/util/config/ConfigFileParser.java b/benchmarks/src/main/kotlin/org/iguana/util/config/ConfigFileParser.java new file mode 100644 index 000000000..5e91a74be --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/util/config/ConfigFileParser.java @@ -0,0 +1,7 @@ +package org.iguana.util.config; + +import org.iguana.util.Configuration; + +public interface ConfigFileParser { + Configuration getConfiguration(); +} diff --git a/benchmarks/src/main/kotlin/org/iguana/util/config/XMLConfigFileParser.java b/benchmarks/src/main/kotlin/org/iguana/util/config/XMLConfigFileParser.java new file mode 100644 index 000000000..abf8d99e5 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/util/config/XMLConfigFileParser.java @@ -0,0 +1,81 @@ +package org.iguana.util.config; + +import org.iguana.util.Configuration; +import org.iguana.utils.logging.LogLevel; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; + +public class XMLConfigFileParser implements ConfigFileParser { + + private Element root; + + private XMLConfigFileParser(Element root) { + this.root = root; + } + + public static XMLConfigFileParser create(String name) throws Exception { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + DocumentBuilder builder = factory.newDocumentBuilder(); + Document doc = builder.parse(Configuration.class.getResourceAsStream("/" + name)); + return new XMLConfigFileParser(doc.getDocumentElement()); + } + + @Override + public Configuration getConfiguration() { + Configuration.Builder builder = Configuration.builder(); + getParserConfigs(builder); + getLoggingConfig(builder); + return builder.build(); + } + + private void getParserConfigs(Configuration.Builder builder) { + Node configurationNode = root.getElementsByTagName("Parser").item(0); + NodeList childNodes = configurationNode.getChildNodes(); + for (int i = 0; i < childNodes.getLength(); i++) { + Node node = childNodes.item(i); + + switch (node.getNodeName()) { + case "Lookahead": + builder.setLookaheadCount(Integer.parseInt(node.getTextContent().toUpperCase())); + break; + + case "GSSLookupImpl": + builder.setGSSLookupImpl(Configuration.LookupImpl.valueOf(node.getTextContent().toUpperCase())); + break; + + case "MatcherType": + builder.setMatcherType(Configuration.MatcherType.valueOf(node.getTextContent().toUpperCase())); + break; + + case "HashMapImpl": + builder.setHashmapImpl(Configuration.HashMapImpl.valueOf(node.getTextContent().toUpperCase())); + break; + + case "EnvironmentImpl": + builder.setEnvironmentImpl( + Configuration.EnvironmentImpl.valueOf(node.getTextContent().toUpperCase())); + break; + } + } + } + + private void getLoggingConfig(Configuration.Builder builder) { + Node configurationNode = root.getElementsByTagName("Logging").item(0); + NodeList childNodes = configurationNode.getChildNodes(); + for (int i = 0; i < childNodes.getLength(); i++) { + Node node = childNodes.item(i); + switch (node.getNodeName()) { + + case "LogLevel": + builder.setLogLevel(LogLevel.valueOf(node.getTextContent().toUpperCase())); + break; + } + } + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/util/serialization/JsonSerializer.java b/benchmarks/src/main/kotlin/org/iguana/util/serialization/JsonSerializer.java new file mode 100644 index 000000000..6d9b6f080 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/util/serialization/JsonSerializer.java @@ -0,0 +1,1061 @@ +package org.iguana.util.serialization; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.core.util.DefaultIndenter; +import com.fasterxml.jackson.core.util.DefaultPrettyPrinter; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.KeyDeserializer; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.module.SimpleModule; +import org.iguana.datadependent.ast.AST; +import org.iguana.datadependent.ast.Expression; +import org.iguana.datadependent.ast.Statement; +import org.iguana.datadependent.ast.VariableDeclaration; +import org.iguana.datadependent.attrs.AbstractAttrs; +import org.iguana.grammar.Grammar; +import org.iguana.grammar.condition.Condition; +import org.iguana.grammar.condition.ConditionType; +import org.iguana.grammar.condition.DataDependentCondition; +import org.iguana.grammar.condition.PositionalCondition; +import org.iguana.grammar.condition.RegularExpressionCondition; +import org.iguana.grammar.runtime.AssociativityGroup; +import org.iguana.grammar.runtime.PrecedenceLevel; +import org.iguana.grammar.runtime.RuntimeGrammar; +import org.iguana.grammar.runtime.RuntimeRule; +import org.iguana.grammar.slot.GrammarSlot; +import org.iguana.grammar.slot.NonterminalNodeType; +import org.iguana.grammar.symbol.Align; +import org.iguana.grammar.symbol.Alt; +import org.iguana.grammar.symbol.Alternative; +import org.iguana.grammar.symbol.Associativity; +import org.iguana.grammar.symbol.Code; +import org.iguana.grammar.symbol.CodeHolder; +import org.iguana.grammar.symbol.Error; +import org.iguana.grammar.symbol.Group; +import org.iguana.grammar.symbol.Identifier; +import org.iguana.grammar.symbol.IfThenElse; +import org.iguana.grammar.symbol.Ignore; +import org.iguana.grammar.symbol.LayoutStrategy; +import org.iguana.grammar.symbol.Nonterminal; +import org.iguana.grammar.symbol.Offside; +import org.iguana.grammar.symbol.Opt; +import org.iguana.grammar.symbol.Plus; +import org.iguana.grammar.symbol.PriorityLevel; +import org.iguana.grammar.symbol.Return; +import org.iguana.grammar.symbol.Rule; +import org.iguana.grammar.symbol.Sequence; +import org.iguana.grammar.symbol.Star; +import org.iguana.grammar.symbol.Start; +import org.iguana.grammar.symbol.Symbol; +import org.iguana.grammar.symbol.Terminal; +import org.iguana.gss.GSSNode; +import org.iguana.parser.ParseError; +import org.iguana.parsetree.AmbiguityNode; +import org.iguana.parsetree.DefaultTerminalNode; +import org.iguana.parsetree.ErrorNode; +import org.iguana.parsetree.KeywordTerminalNode; +import org.iguana.parsetree.MetaSymbolNode; +import org.iguana.parsetree.NonterminalNode; +import org.iguana.parsetree.ParseTreeNode; +import org.iguana.regex.Char; +import org.iguana.regex.CharRange; +import org.iguana.regex.Epsilon; +import org.iguana.regex.Reference; +import org.iguana.regex.RegularExpression; +import org.iguana.regex.Seq; +import org.iguana.regex.automaton.Automaton; +import org.iguana.utils.input.Input; + +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.Writer; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public class JsonSerializer { + + private static final ObjectMapper mapper = new ObjectMapper(); + + static { + mapper.setVisibility(mapper.getSerializationConfig().getDefaultVisibilityChecker() + .withFieldVisibility(JsonAutoDetect.Visibility.ANY) + .withGetterVisibility(JsonAutoDetect.Visibility.NONE) + .withIsGetterVisibility(JsonAutoDetect.Visibility.NONE)); + + mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); + + mapper.addMixIn(RuntimeGrammar.class, RuntimeGrammarMixIn.class); + mapper.addMixIn(RuntimeRule.class, RuntimeRuleMixIn.class); + + mapper.addMixIn(Grammar.class, GrammarMixIn.class); + mapper.addMixIn(Rule.class, RuleMixIn.class); + mapper.addMixIn(PriorityLevel.class, PriorityLevelMixIn.class); + mapper.addMixIn(Alternative.class, AlternativeMixIn.class); + mapper.addMixIn(Sequence.class, SequenceMixIn.class); + + mapper.addMixIn(Symbol.class, SymbolMixIn.class); + mapper.addMixIn(Nonterminal.class, NonterminalMixIn.class); + mapper.addMixIn(Terminal.class, TerminalMixIn.class); + mapper.addMixIn(Star.class, StarMixIn.class); + mapper.addMixIn(Plus.class, PlusMixIn.class); + mapper.addMixIn(Opt.class, OptMixIn.class); + mapper.addMixIn(Alt.class, AltMixIn.class); + mapper.addMixIn(Group.class, GroupMixIn.class); + mapper.addMixIn(Identifier.class, IdentifierMixIn.class); + mapper.addMixIn(Start.class, StartMixIn.class); + mapper.addMixIn(Code.class, CodeMixIn.class); + mapper.addMixIn(CodeHolder.class, CodeHolderMixIn.class); + mapper.addMixIn(Return.class, ReturnMixIn.class); + mapper.addMixIn(IfThenElse.class, IfThenElseMixIn.class); + mapper.addMixIn(Offside.class, OffsideMixIn.class); + mapper.addMixIn(Align.class, AlignMixIn.class); + mapper.addMixIn(Ignore.class, IgnoreMixIn.class); + mapper.addMixIn(Error.class, ErrorMixIn.class); + + mapper.addMixIn(AbstractAttrs.class, AbstractAttrsMixIn.class); + + // Conditions + mapper.addMixIn(Condition.class, ConditionMixIn.class); + mapper.addMixIn(RegularExpressionCondition.class, RegularExpressionConditionMixIn.class); + mapper.addMixIn(DataDependentCondition.class, DataDependentConditionMixIn.class); + mapper.addMixIn(PositionalCondition.class, PositionalConditionMixIn.class); + + // Expression + mapper.addMixIn(Expression.class, ExpressionMixIn.class); + mapper.addMixIn(Expression.Integer.class, ExpressionMixIn.IntegerMixIn.class); + mapper.addMixIn(Expression.Real.class, ExpressionMixIn.RealMixIn.class); + mapper.addMixIn(Expression.String.class, ExpressionMixIn.StringMixIn.class); + mapper.addMixIn(Expression.Tuple.class, ExpressionMixIn.TupleMixIn.class); + mapper.addMixIn(Expression.Name.class, ExpressionMixIn.NameMixIn.class); + mapper.addMixIn(Expression.Assignment.class, ExpressionMixIn.AssignmentMixIn.class); + mapper.addMixIn(Expression.LShiftANDEqZero.class, ExpressionMixIn.LShiftANDEqZeroMixIn.class); + mapper.addMixIn(Expression.OrIndent.class, ExpressionMixIn.OrIndentMixIn.class); + mapper.addMixIn(Expression.AndIndent.class, ExpressionMixIn.AndIndentMixIn.class); + mapper.addMixIn(Expression.Not.class, ExpressionMixIn.NotMixIn.class); + mapper.addMixIn(Expression.Or.class, ExpressionMixIn.OrMixIn.class); + mapper.addMixIn(Expression.And.class, ExpressionMixIn.AndMixIn.class); + mapper.addMixIn(Expression.Add.class, ExpressionMixIn.AddMixIn.class); + mapper.addMixIn(Expression.Subtract.class, ExpressionMixIn.SubtractMixIn.class); + mapper.addMixIn(Expression.Multiply.class, ExpressionMixIn.MultiplyMixIn.class); + mapper.addMixIn(Expression.Divide.class, ExpressionMixIn.DivideMixIn.class); + mapper.addMixIn(Expression.Less.class, ExpressionMixIn.LessMixIn.class); + mapper.addMixIn(Expression.LessThanEqual.class, ExpressionMixIn.LessThanEqualMixIn.class); + mapper.addMixIn(Expression.Greater.class, ExpressionMixIn.GreaterMixIn.class); + mapper.addMixIn(Expression.GreaterThanEqual.class, ExpressionMixIn.GreaterThanEqualMixIn.class); + mapper.addMixIn(Expression.Equal.class, ExpressionMixIn.EqualMixIn.class); + mapper.addMixIn(Expression.NotEqual.class, ExpressionMixIn.NotEqualMixIn.class); + mapper.addMixIn(Expression.LeftExtent.class, ExpressionMixIn.LeftExtentMixIn.class); + mapper.addMixIn(Expression.RightExtent.class, ExpressionMixIn.RightExtentMixIn.class); + mapper.addMixIn(Expression.Yield.class, ExpressionMixIn.YieldMixIn.class); + mapper.addMixIn(Expression.Val.class, ExpressionMixIn.ValMixIn.class); + mapper.addMixIn(Expression.EndOfFile.class, ExpressionMixIn.EndOfFileMixIn.class); + mapper.addMixIn(Expression.IfThenElse.class, ExpressionMixIn.IfThenElseMixIn.class); + mapper.addMixIn(Expression.Call.class, ExpressionMixIn.CallMixIn.class); + + mapper.addMixIn(VariableDeclaration.class, VariableDeclarationMixIn.class); + + // Call Expressions + mapper.addMixIn(AST.Println.class, ExpressionMixIn.CallMixIn.PrintlnMixIn.class); + mapper.addMixIn(AST.Assert.class, ExpressionMixIn.CallMixIn.AssertMixIn.class); + mapper.addMixIn(AST.Set.class, ExpressionMixIn.CallMixIn.SetMixIn.class); + mapper.addMixIn(AST.StartsWith.class, ExpressionMixIn.CallMixIn.StartsWithMixIn.class); + mapper.addMixIn(AST.Push.class, ExpressionMixIn.CallMixIn.PushMixIn.class); + mapper.addMixIn(AST.Shift.class, ExpressionMixIn.CallMixIn.ShiftMixIn.class); + mapper.addMixIn(AST.Println.class, ExpressionMixIn.CallMixIn.PrintlnMixIn.class); + mapper.addMixIn(AST.Indent.class, ExpressionMixIn.CallMixIn.IndentMixIn.class); + mapper.addMixIn(AST.Min.class, ExpressionMixIn.CallMixIn.MinMixIn.class); + mapper.addMixIn(AST.Neg.class, ExpressionMixIn.CallMixIn.NegMixIn.class); + mapper.addMixIn(AST.Pop.class, ExpressionMixIn.CallMixIn.PopMixIn.class); + mapper.addMixIn(AST.Get.class, ExpressionMixIn.CallMixIn.GetMixIn.class); + mapper.addMixIn(AST.Len.class, ExpressionMixIn.CallMixIn.LenMixIn.class); + mapper.addMixIn(AST.Top.class, ExpressionMixIn.CallMixIn.TopMixIn.class); + mapper.addMixIn(AST.Get2.class, ExpressionMixIn.CallMixIn.Get2MixIn.class); + mapper.addMixIn(AST.Put.class, ExpressionMixIn.CallMixIn.PutMixIn.class); + mapper.addMixIn(AST.Contains.class, ExpressionMixIn.CallMixIn.ContainsMixIn.class); + mapper.addMixIn(AST.PPDeclare.class, ExpressionMixIn.CallMixIn.PPDeclareMixIn.class); + mapper.addMixIn(AST.Put3.class, ExpressionMixIn.CallMixIn.Put3MixIn.class); + mapper.addMixIn(AST.EndsWith.class, ExpressionMixIn.CallMixIn.EndsWithMixIn.class); + mapper.addMixIn(AST.Map.class, ExpressionMixIn.CallMixIn.MapMixIn.class); + mapper.addMixIn(AST.Pr2.class, ExpressionMixIn.CallMixIn.Pr2MixIn.class); + mapper.addMixIn(AST.Pr3.class, ExpressionMixIn.CallMixIn.Pr3MixIn.class); + mapper.addMixIn(AST.PPLookup.class, ExpressionMixIn.CallMixIn.PPLookupMixIn.class); + mapper.addMixIn(AST.Undef.class, ExpressionMixIn.CallMixIn.UndefMixIn.class); + mapper.addMixIn(AST.Find.class, ExpressionMixIn.CallMixIn.FindMixIn.class); + mapper.addMixIn(AST.Pr1.class, ExpressionMixIn.CallMixIn.Pr1MixIn.class); + + + // Statement + mapper.addMixIn(Statement.class, StatementMixIn.class); + mapper.addMixIn(Statement.Expression.class, StatementMixIn.ExpressionMixIn.class); + mapper.addMixIn(Statement.VariableDeclaration.class, StatementMixIn.VariableDeclarationMixIn.class); + + // Regex + mapper.addMixIn(RegularExpression.class, RegularExpressionMixIn.class); + mapper.addMixIn(Seq.class, SeqMixIn.class); + mapper.addMixIn(org.iguana.regex.Alt.class, RegexAltMixIn.class); + mapper.addMixIn(org.iguana.regex.Star.class, RegexStarMixIn.class); + mapper.addMixIn(org.iguana.regex.Plus.class, RegexPlusMixIn.class); + mapper.addMixIn(org.iguana.regex.Opt.class, RegexOptMixIn.class); + mapper.addMixIn(Char.class, CharMixIn.class); + mapper.addMixIn(CharRange.class, CharRangeMixIn.class); + mapper.addMixIn(Reference.class, ReferenceMixIn.class); + mapper.addMixIn(Epsilon.class, EpsilonMixIn.class); + + // Parse tree + mapper.addMixIn(ParseTreeNode.class, ParseTreeNodeMixIn.class); + mapper.addMixIn(NonterminalNode.class, NonterminalNodeMixIn.class); + mapper.addMixIn(AmbiguityNode.class, AmbiguityNodeMixIn.class); + mapper.addMixIn(MetaSymbolNode.StarNode.class, StarNodeMixIn.class); + mapper.addMixIn(MetaSymbolNode.PlusNode.class, PlusNodeMixIn.class); + mapper.addMixIn(MetaSymbolNode.GroupNode.class, GroupNodeMixIn.class); + mapper.addMixIn(MetaSymbolNode.AltNode.class, AltNodeMixIn.class); + mapper.addMixIn(MetaSymbolNode.OptionNode.class, OptionNodeMixIn.class); + mapper.addMixIn(MetaSymbolNode.StartNode.class, StartNodeMixIn.class); + mapper.addMixIn(DefaultTerminalNode.class, DefaultTerminalNodeMixIn.class); + mapper.addMixIn(KeywordTerminalNode.class, KeywordTerminalNodeMixIn.class); + mapper.addMixIn(ErrorNode.class, ErrorNodeMixIn.class); + + mapper.addMixIn(ParseError.class, ParseErrorMixIn.class); + + mapper.addMixIn(PrecedenceLevel.class, PrecedenceLevelMixIn.class); + mapper.addMixIn(AssociativityGroup.class, AssociativityGroupMixIn.class); + + // Since @JsonIgnore doesn't work on a map property with non-string keys and Jackson + // tries to deserialize the definitions field. This is a solution to let Jackson know + // about the Nonterminal key type. + class NonterminalKeyDeserializer extends KeyDeserializer { + @Override + public Object deserializeKey(String key, DeserializationContext ctxt) throws IOException { + return mapper.readValue(key, Nonterminal.class); + } + } + + SimpleModule module = new SimpleModule(); + module.addKeyDeserializer(Nonterminal.class, new NonterminalKeyDeserializer()); + mapper.registerModule(module); + } + + public static String toJSON(Grammar grammar) { + return serialize(grammar); + } + + public static String toJSON(ParseTreeNode node) { + return serialize(node); + } + + public static void serialize(Object obj, String path) throws IOException { + OutputStream out = new FileOutputStream(path); + try (Writer writer = new OutputStreamWriter(out)) { + DefaultPrettyPrinter pp = new DefaultPrettyPrinter(); + pp.indentArraysWith(DefaultIndenter.SYSTEM_LINEFEED_INSTANCE); + mapper.writer(pp).writeValue(writer, obj); + } + } + + public static String serialize(Object obj) { + DefaultPrettyPrinter pp = new DefaultPrettyPrinter(); + pp.indentArraysWith(DefaultIndenter.SYSTEM_LINEFEED_INSTANCE); + try { + return mapper.writer(pp).writeValueAsString(obj); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public static T deserialize(String jsonContent, Class clazz) throws IOException { + return mapper.readValue(jsonContent, clazz); + } + + public static T deserialize(InputStream in, Class clazz) throws IOException { + return mapper.readValue(in, clazz); + } + + static class LayoutStrategyFilter { + @Override + public boolean equals(Object obj) { + return obj == LayoutStrategy.INHERITED; + } + + @Override + public int hashCode() { + return LayoutStrategy.INHERITED.hashCode(); + } + } + + @JsonDeserialize(builder = RuntimeGrammar.Builder.class) + abstract static class RuntimeGrammarMixIn { + @JsonIgnore + Map> definitions; + @JsonIgnore + Map terminals; + @JsonIgnore + Set literals; + } + + @JsonDeserialize(builder = RuntimeRule.Builder.class) + abstract static class RuntimeRuleMixIn { + @JsonInclude(value = JsonInclude.Include.CUSTOM, valueFilter = LayoutStrategyFilter.class) + LayoutStrategy layoutStrategy; + @JsonIgnore + Map attributes; + } + + @JsonDeserialize(builder = Grammar.Builder.class) + abstract static class GrammarMixIn { + @JsonIgnore + RuntimeGrammar runtimeGrammar; + } + + @JsonDeserialize(builder = Rule.Builder.class) + abstract static class RuleMixIn { + @JsonIgnore + Map attributes; + } + + abstract static class AnnotationMixIn { + @JsonCreator + AnnotationMixIn( + @JsonProperty("name") String name, + @JsonProperty("value") String value, + @JsonProperty("symbolName") String symbolName) {} + } + + @JsonDeserialize(builder = PriorityLevel.Builder.class) + abstract static class PriorityLevelMixIn {} + + @JsonDeserialize(builder = Alternative.Builder.class) + abstract static class AlternativeMixIn {} + + @JsonDeserialize(builder = Sequence.Builder.class) + abstract static class SequenceMixIn { + @JsonIgnore + Map attributes; + } + + @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "kind") + @JsonSubTypes({ + @JsonSubTypes.Type(value = Nonterminal.class, name = "Nonterminal"), + @JsonSubTypes.Type(value = Terminal.class, name = "Terminal"), + @JsonSubTypes.Type(value = Star.class, name = "Star"), + @JsonSubTypes.Type(value = Plus.class, name = "Plus"), + @JsonSubTypes.Type(value = Opt.class, name = "Opt"), + @JsonSubTypes.Type(value = Alt.class, name = "Alt"), + @JsonSubTypes.Type(value = Group.class, name = "Sequence"), + @JsonSubTypes.Type(value = Start.class, name = "Start"), + @JsonSubTypes.Type(value = Identifier.class, name = "Identifier"), + @JsonSubTypes.Type(value = CodeHolder.class, name = "CodeHolder"), + @JsonSubTypes.Type(value = Code.class, name = "Code"), + @JsonSubTypes.Type(value = Return.class, name = "Return"), + @JsonSubTypes.Type(value = IfThenElse.class, name = "IfThenElse"), + @JsonSubTypes.Type(value = Offside.class, name = "Offside"), + @JsonSubTypes.Type(value = Align.class, name = "Align"), + @JsonSubTypes.Type(value = Ignore.class, name = "Ignore"), + @JsonSubTypes.Type(value = Error.class, name = "Error") + }) + abstract static class SymbolMixIn {} + + abstract static class AbstractSymbolMixIn extends SymbolMixIn { + @JsonIgnore + Map attributes; + } + + @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "kind") + @JsonSubTypes({ + @JsonSubTypes.Type(value = org.iguana.regex.Seq.class, name = "regex.Seq"), + @JsonSubTypes.Type(value = org.iguana.regex.Alt.class, name = "regex.Alt"), + @JsonSubTypes.Type(value = org.iguana.regex.Star.class, name = "regex.Star"), + @JsonSubTypes.Type(value = org.iguana.regex.Plus.class, name = "regex.Plus"), + @JsonSubTypes.Type(value = org.iguana.regex.Opt.class, name = "regex.Opt"), + @JsonSubTypes.Type(value = org.iguana.regex.Char.class, name = "Char"), + @JsonSubTypes.Type(value = org.iguana.regex.CharRange.class, name = "CharRange"), + @JsonSubTypes.Type(value = org.iguana.regex.Reference.class, name = "Reference"), + @JsonSubTypes.Type(value = org.iguana.regex.Epsilon.class, name = "Epsilon") + }) + abstract static class RegularExpressionMixIn {} + + abstract static class AbstractRegularExpressionMixIn { + @JsonIgnore + Automaton automaton; + } + + @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "kind") + @JsonSubTypes({ + @JsonSubTypes.Type(value = DefaultTerminalNode.class, name = "TerminalNode"), + @JsonSubTypes.Type(value = KeywordTerminalNode.class, name = "KeywordTerminalNode"), + @JsonSubTypes.Type(value = NonterminalNode.class, name = "NonterminalNode"), + @JsonSubTypes.Type(value = AmbiguityNode.class, name = "AmbiguityNode"), + @JsonSubTypes.Type(value = MetaSymbolNode.StarNode.class, name = "StarNode"), + @JsonSubTypes.Type(value = MetaSymbolNode.PlusNode.class, name = "PlusNode"), + @JsonSubTypes.Type(value = MetaSymbolNode.GroupNode.class, name = "GroupNode"), + @JsonSubTypes.Type(value = MetaSymbolNode.OptionNode.class, name = "OptionNode"), + @JsonSubTypes.Type(value = MetaSymbolNode.AltNode.class, name = "AltNode"), + @JsonSubTypes.Type(value = MetaSymbolNode.StartNode.class, name = "StartNode"), + @JsonSubTypes.Type(value = ErrorNode.class, name = "ErrorNode") + }) + abstract static class ParseTreeNodeMixIn {} + + abstract static class DefaultTerminalNodeMixIn { + @JsonIgnore + Input input; + + DefaultTerminalNodeMixIn( + @JsonProperty("terminal") Terminal terminal, + @JsonProperty("start") int start, + @JsonProperty("end") int end, + @JsonProperty("input") Input input) {} + + } + + abstract static class KeywordTerminalNodeMixIn { + KeywordTerminalNodeMixIn( + @JsonProperty("terminal") Terminal terminal, + @JsonProperty("start") int start, + @JsonProperty("end") int end) {} + } + + abstract static class ErrorNodeMixIn { + @JsonIgnore + Input input; + + ErrorNodeMixIn( + @JsonProperty("start") int start, + @JsonProperty("end") int end, + @JsonProperty("input") Input input) {} + } + + abstract static class NonterminalNodeMixIn { + NonterminalNodeMixIn( + @JsonProperty("rule") RuntimeRule rule, + @JsonProperty("children") List children, + @JsonProperty("start") int start, + @JsonProperty("end") int end) {} + } + + abstract static class StarNodeMixIn { + StarNodeMixIn( + @JsonProperty("symbol") Star symbol, + @JsonProperty("children") List children, + @JsonProperty("start") int start, + @JsonProperty("end") int end) {} + } + + abstract static class PlusNodeMixIn { + PlusNodeMixIn( + @JsonProperty("symbol") Plus symbol, + @JsonProperty("children") List children, + @JsonProperty("start") int start, + @JsonProperty("end") int end) {} + } + + abstract static class GroupNodeMixIn { + GroupNodeMixIn( + @JsonProperty("symbol") Group symbol, + @JsonProperty("children") List children, + @JsonProperty("start") int start, + @JsonProperty("end") int end) {} + } + + abstract static class OptionNodeMixIn { + OptionNodeMixIn( + @JsonProperty("symbol") Opt symbol, + @JsonProperty("child") ParseTreeNode child, + @JsonProperty("start") int start, + @JsonProperty("end") int end) {} + } + + abstract static class AltNodeMixIn { + AltNodeMixIn( + @JsonProperty("symbol") Alt symbol, + @JsonProperty("child") ParseTreeNode child, + @JsonProperty("start") int start, + @JsonProperty("end") int end) {} + } + + abstract static class StartNodeMixIn { + StartNodeMixIn( + @JsonProperty("symbol") Start symbol, + @JsonProperty("children") List children, + @JsonProperty("start") int start, + @JsonProperty("end") int end) {} + } + + abstract static class AmbiguityNodeMixIn { + AmbiguityNodeMixIn(@JsonProperty("alternatives") Set alternatives) {} + } + + @JsonDeserialize(builder = Nonterminal.Builder.class) + abstract static class NonterminalMixIn extends AbstractSymbolMixIn { + + @JsonInclude(JsonInclude.Include.NON_DEFAULT) + int index; + + @JsonInclude(JsonInclude.Include.NON_DEFAULT) + boolean ebnfList; + + @JsonInclude(value = JsonInclude.Include.CUSTOM, valueFilter = NonterminalNodeTypeFilter.class) + NonterminalNodeType nodeType; + + @JsonIgnore + Map attributes; + + private static class NonterminalNodeTypeFilter { + @Override + public boolean equals(Object obj) { + return obj == NonterminalNodeType.Basic; + } + } + } + + @JsonDeserialize(builder = Star.Builder.class) + abstract static class StarMixIn extends AbstractSymbolMixIn {} + + @JsonDeserialize(builder = Start.Builder.class) + abstract static class StartMixIn extends AbstractSymbolMixIn {} + + @JsonDeserialize(builder = Code.Builder.class) + abstract static class CodeMixIn extends AbstractSymbolMixIn {} + + @JsonDeserialize(builder = CodeHolder.Builder.class) + abstract static class CodeHolderMixIn extends AbstractSymbolMixIn {} + + @JsonDeserialize(builder = Plus.Builder.class) + abstract static class PlusMixIn extends AbstractSymbolMixIn {} + + @JsonDeserialize(builder = Opt.Builder.class) + abstract static class OptMixIn extends AbstractSymbolMixIn {} + + @JsonDeserialize(builder = Alt.Builder.class) + abstract static class AltMixIn extends AbstractSymbolMixIn {} + + @JsonDeserialize(builder = Group.Builder.class) + abstract static class GroupMixIn extends AbstractSymbolMixIn {} + + @JsonDeserialize(builder = Identifier.Builder.class) + abstract static class IdentifierMixIn extends AbstractSymbolMixIn {} + + @JsonDeserialize(builder = Terminal.Builder.class) + abstract static class TerminalMixIn extends AbstractSymbolMixIn {} + + @JsonDeserialize(builder = Return.Builder.class) + abstract static class ReturnMixIn extends AbstractSymbolMixIn {} + + @JsonDeserialize(builder = IfThenElse.Builder.class) + abstract static class IfThenElseMixIn extends AbstractSymbolMixIn {} + + @JsonDeserialize(builder = Offside.Builder.class) + abstract static class OffsideMixIn extends AbstractSymbolMixIn {} + + @JsonDeserialize(builder = Align.Builder.class) + abstract static class AlignMixIn extends AbstractSymbolMixIn {} + + @JsonDeserialize(builder = Ignore.Builder.class) + abstract static class IgnoreMixIn extends AbstractSymbolMixIn {} + + abstract static class ErrorMixIn extends AbstractAttrsMixIn { + @JsonCreator + public abstract Error getInstance(); + } + + @JsonDeserialize(builder = org.iguana.regex.Seq.Builder.class) + abstract static class SeqMixIn extends AbstractRegularExpressionMixIn {} + + @JsonDeserialize(builder = org.iguana.regex.Alt.Builder.class) + abstract static class RegexAltMixIn extends AbstractRegularExpressionMixIn {} + + @JsonDeserialize(builder = org.iguana.regex.Star.Builder.class) + abstract static class RegexStarMixIn extends AbstractRegularExpressionMixIn {} + + @JsonDeserialize(builder = org.iguana.regex.Plus.Builder.class) + abstract static class RegexPlusMixIn extends AbstractRegularExpressionMixIn {} + + @JsonDeserialize(builder = org.iguana.regex.Opt.Builder.class) + abstract static class RegexOptMixIn extends AbstractRegularExpressionMixIn {} + + @JsonDeserialize(builder = org.iguana.regex.Char.Builder.class) + abstract static class CharMixIn extends AbstractRegularExpressionMixIn {} + + @JsonDeserialize(builder = org.iguana.regex.CharRange.Builder.class) + abstract static class CharRangeMixIn extends AbstractRegularExpressionMixIn {} + + @JsonDeserialize(builder = org.iguana.regex.Reference.Builder.class) + abstract static class ReferenceMixIn extends AbstractRegularExpressionMixIn {} + + abstract static class EpsilonMixIn extends AbstractRegularExpressionMixIn { + @JsonCreator + public abstract Epsilon getInstance(); + } + + @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "kind") + @JsonSubTypes({ + @JsonSubTypes.Type(value = RegularExpressionCondition.class, name = "RegularExpressionCondition"), + @JsonSubTypes.Type(value = DataDependentCondition.class, name = "DataDependentCondition"), + @JsonSubTypes.Type(value = PositionalCondition.class, name = "PositionalCondition") + }) + abstract static class ConditionMixIn {} + + abstract static class VariableDeclarationMixIn { + @JsonCreator + VariableDeclarationMixIn( + @JsonProperty("name") String name, + @JsonProperty("i") int i, + @JsonProperty("expression") Expression expression) {} + } + + @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "kind") + @JsonSubTypes({ + @JsonSubTypes.Type(value = Expression.Integer.class, name = "Integer"), + @JsonSubTypes.Type(value = Expression.Real.class, name = "Real"), + @JsonSubTypes.Type(value = Expression.String.class, name = "String"), + @JsonSubTypes.Type(value = Expression.Name.class, name = "Name"), + @JsonSubTypes.Type(value = Expression.Assignment.class, name = "Assignment"), + @JsonSubTypes.Type(value = Expression.Tuple.class, name = "Tuple"), + @JsonSubTypes.Type(value = Expression.GreaterThanEqual.class, name = "GreaterThanEqual"), + @JsonSubTypes.Type(value = Expression.LShiftANDEqZero.class, name = "LShiftANDEqZero"), + @JsonSubTypes.Type(value = Expression.Greater.class, name = "Greater"), + @JsonSubTypes.Type(value = Expression.Not.class, name = "Not"), + @JsonSubTypes.Type(value = Expression.Or.class, name = "Or"), + @JsonSubTypes.Type(value = Expression.LessThanEqual.class, name = "LessThanEqual"), + @JsonSubTypes.Type(value = Expression.IfThenElse.class, name = "IfThenElse"), + @JsonSubTypes.Type(value = Expression.Equal.class, name = "Equal"), + @JsonSubTypes.Type(value = Expression.NotEqual.class, name = "NotEqual"), + @JsonSubTypes.Type(value = Expression.OrIndent.class, name = "OrIndent"), + @JsonSubTypes.Type(value = Expression.AndIndent.class, name = "AndIndent"), + @JsonSubTypes.Type(value = Expression.And.class, name = "And"), + @JsonSubTypes.Type(value = Expression.Add.class, name = "Add"), + @JsonSubTypes.Type(value = Expression.LeftExtent.class, name = "LeftExtent"), + @JsonSubTypes.Type(value = Expression.RightExtent.class, name = "RightExtent"), + @JsonSubTypes.Type(value = Expression.Yield.class, name = "Yield"), + @JsonSubTypes.Type(value = Expression.Val.class, name = "Val"), + @JsonSubTypes.Type(value = Expression.EndOfFile.class, name = "EndOfFile"), + @JsonSubTypes.Type(value = Expression.Call.class, name = "Call") + }) + abstract static class ExpressionMixIn { + + abstract static class IntegerMixIn { + @JsonCreator + IntegerMixIn(@JsonProperty("value") Integer value) {} + } + + abstract static class RealMixIn { + @JsonCreator + RealMixIn(@JsonProperty("value") Float value) {} + } + + abstract static class StringMixIn { + @JsonCreator + StringMixIn(@JsonProperty("value") String value) {} + } + + abstract static class NameMixIn { + @JsonCreator + NameMixIn(@JsonProperty("name") String name, @JsonProperty("i") int i) {} + } + + abstract static class AssignmentMixIn { + @JsonCreator + AssignmentMixIn(@JsonProperty("id") String id, @JsonProperty("exp") Expression exp) {} + } + + abstract static class TupleMixIn { + @JsonCreator + TupleMixIn(@JsonProperty("elements") Expression... elements) {} + } + + abstract static class IfThenElseMixIn { + @JsonCreator + IfThenElseMixIn( + @JsonProperty("condition") Expression condition, + @JsonProperty("thenPart") Expression thenPart, + @JsonProperty("elsePart") Expression elsePart) {} + } + + @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "kind") + @JsonSubTypes({ + @JsonSubTypes.Type(value = AST.StartsWith.class, name = "StartsWith"), + @JsonSubTypes.Type(value = AST.Push.class, name = "Push"), + @JsonSubTypes.Type(value = AST.Pop.class, name = "Pop"), + @JsonSubTypes.Type(value = AST.Top.class, name = "Top"), + @JsonSubTypes.Type(value = AST.Find.class, name = "Find"), + @JsonSubTypes.Type(value = AST.Shift.class, name = "Shift"), + @JsonSubTypes.Type(value = AST.Println.class, name = "Println"), + @JsonSubTypes.Type(value = AST.Assert.class, name = "Assert"), + @JsonSubTypes.Type(value = AST.Set.class, name = "Set"), + @JsonSubTypes.Type(value = AST.Indent.class, name = "Indent"), + @JsonSubTypes.Type(value = AST.Min.class, name = "Min"), + @JsonSubTypes.Type(value = AST.Neg.class, name = "Neg"), + @JsonSubTypes.Type(value = AST.Get.class, name = "Get"), + @JsonSubTypes.Type(value = AST.Len.class, name = "Len"), + @JsonSubTypes.Type(value = AST.Get2.class, name = "Get2"), + @JsonSubTypes.Type(value = AST.Put.class, name = "Put"), + @JsonSubTypes.Type(value = AST.Contains.class, name = "Contains"), + @JsonSubTypes.Type(value = AST.PPDeclare.class, name = "PPDeclare"), + @JsonSubTypes.Type(value = AST.Put3.class, name = "Put3"), + @JsonSubTypes.Type(value = AST.EndsWith.class, name = "EndsWith"), + @JsonSubTypes.Type(value = AST.Map.class, name = "Map"), + @JsonSubTypes.Type(value = AST.Pr2.class, name = "Pr2"), + @JsonSubTypes.Type(value = AST.Pr3.class, name = "Pr3"), + @JsonSubTypes.Type(value = AST.PPLookup.class, name = "PPLookup"), + @JsonSubTypes.Type(value = AST.Undef.class, name = "Undef"), + @JsonSubTypes.Type(value = AST.Pr1.class, name = "Pr1") + }) + abstract static class CallMixIn { + + abstract static class PrintlnMixIn { + @JsonCreator + PrintlnMixIn(@JsonProperty("arguments") Expression[] arguments) {} + } + + abstract static class AssertMixIn { + @JsonCreator + AssertMixIn(@JsonProperty("arguments") Expression[] arguments) {} + } + + abstract static class SetMixIn { + @JsonCreator + SetMixIn(@JsonProperty("arguments") Expression[] arguments) {} + } + + abstract static class StartsWithMixIn { + @JsonCreator + StartsWithMixIn(@JsonProperty("arguments") Expression[] arguments) {} + } + + abstract static class PushMixIn { + @JsonCreator + PushMixIn(@JsonProperty("arguments") Expression[] arguments) {} + } + + abstract static class PopMixIn { + @JsonCreator + PopMixIn(@JsonProperty("arguments") Expression[] arguments) {} + } + + abstract static class TopMixIn { + @JsonCreator + TopMixIn(@JsonProperty("arguments") Expression[] arguments) {} + } + + abstract static class FindMixIn { + @JsonCreator + FindMixIn(@JsonProperty("arguments") Expression[] arguments) {} + } + + abstract static class ShiftMixIn { + @JsonCreator + ShiftMixIn(@JsonProperty("arguments") Expression[] arguments) {} + } + + abstract static class IndentMixIn { + @JsonCreator + IndentMixIn(@JsonProperty("argument") Expression argument) {} + } + + abstract static class MinMixIn { + @JsonCreator + MinMixIn(@JsonProperty("arguments") Expression[] arguments) {} + } + + abstract static class NotMixIn { + @JsonCreator + NotMixIn(@JsonProperty("arguments") Expression[] arguments) {} + } + + abstract static class NegMixIn { + @JsonCreator + NegMixIn(@JsonProperty("arguments") Expression[] arguments) {} + } + + abstract static class GetMixIn { + @JsonCreator + GetMixIn(@JsonProperty("arg1") Expression arg1, @JsonProperty("arg2") int arg2) {} + } + + abstract static class LenMixIn { + @JsonCreator + LenMixIn(@JsonProperty("arguments") Expression[] arguments) {} + } + + abstract static class Get2MixIn { + @JsonCreator + Get2MixIn(@JsonProperty("arguments") Expression[] arguments) {} + } + + abstract static class PutMixIn { + @JsonCreator + PutMixIn(@JsonProperty("arguments") Expression[] arguments) {} + } + + abstract static class ContainsMixIn { + @JsonCreator + ContainsMixIn(@JsonProperty("arguments") Expression[] arguments) {} + } + + abstract static class PPDeclareMixIn { + @JsonCreator + PPDeclareMixIn(@JsonProperty("variable") Expression variable, @JsonProperty("value") Expression value) { + } + } + + abstract static class Put3MixIn { + @JsonCreator + Put3MixIn(@JsonProperty("arguments") Expression[] arguments) {} + } + + abstract static class EndsWithMixIn { + @JsonCreator + EndsWithMixIn(@JsonProperty("arguments") Expression[] arguments) {} + } + + abstract static class MapMixIn { + @JsonCreator + MapMixIn(@JsonProperty("arguments") Expression[] arguments) {} + } + + abstract static class Pr2MixIn { + @JsonCreator + Pr2MixIn( + @JsonProperty("arg1") Expression arg1, + @JsonProperty("arg2") Expression arg2, + @JsonProperty("arg3") Expression[] arg3) {} + } + + abstract static class Pr3MixIn { + @JsonCreator + Pr3MixIn(@JsonProperty("arguments") Expression[] arguments) {} + } + + abstract static class PPLookupMixIn { + @JsonCreator + PPLookupMixIn(@JsonProperty("arguments") Expression[] arguments) {} + } + + abstract static class UndefMixIn { + @JsonCreator + UndefMixIn(@JsonProperty("arguments") Expression[] arguments) {} + } + + abstract static class Pr1MixIn { + @JsonCreator + Pr1MixIn(@JsonProperty("arguments") Expression[] arguments) {} + } + } + + abstract static class OrIndentMixIn { + @JsonCreator + OrIndentMixIn( + @JsonProperty("index") Expression index, + @JsonProperty("ind") Expression ind, + @JsonProperty("first") Expression first, + @JsonProperty("lExt") Expression lExt) {} + } + + abstract static class AndIndentMixIn { + @JsonCreator + AndIndentMixIn( + @JsonProperty("index") Expression index, + @JsonProperty("first") Expression first, + @JsonProperty("lExt") Expression lExt) {} + } + + abstract static class BinaryExpressionMixIn { + @JsonIgnore + java.lang.String symbolName; + } + + abstract static class AndMixIn extends BinaryExpressionMixIn { + @JsonCreator + AndMixIn( + @JsonProperty("lhs") Expression lhs, + @JsonProperty("rhs") Expression rhs) {} + } + + abstract static class LShiftANDEqZeroMixIn { + @JsonCreator + LShiftANDEqZeroMixIn(@JsonProperty("lhs") Expression lhs, @JsonProperty("rhs") Expression rhs) {} + } + + abstract static class GreaterThanEqualMixIn extends BinaryExpressionMixIn { + @JsonCreator + GreaterThanEqualMixIn(@JsonProperty("lhs") Expression lhs, @JsonProperty("rhs") Expression rhs) {} + } + + abstract static class EqualMixIn extends BinaryExpressionMixIn { + @JsonCreator + EqualMixIn(@JsonProperty("lhs") Expression lhs, @JsonProperty("rhs") Expression rhs) {} + } + + abstract static class NotEqualMixIn extends BinaryExpressionMixIn { + @JsonCreator + NotEqualMixIn(@JsonProperty("lhs") Expression lhs, @JsonProperty("rhs") Expression rhs) {} + } + + abstract static class GreaterMixIn extends BinaryExpressionMixIn { + @JsonCreator + GreaterMixIn(@JsonProperty("lhs") Expression lhs, @JsonProperty("rhs") Expression rhs) {} + } + + abstract static class NotMixIn { + @JsonCreator + NotMixIn(@JsonProperty("exp") Expression exp) {} + } + + abstract static class OrMixIn extends BinaryExpressionMixIn { + @JsonCreator + OrMixIn(@JsonProperty("lhs") Expression lhs, @JsonProperty("rhs") Expression rhs) {} + } + + abstract static class LessThanEqualMixIn extends BinaryExpressionMixIn { + @JsonCreator + LessThanEqualMixIn(@JsonProperty("lhs") Expression lhs, @JsonProperty("rhs") Expression rhs) {} + } + + abstract static class LessMixIn extends BinaryExpressionMixIn { + @JsonCreator + LessMixIn(@JsonProperty("lhs") Expression lhs, @JsonProperty("rhs") Expression rhs) {} + } + + abstract static class AddMixIn extends BinaryExpressionMixIn { + @JsonCreator + AddMixIn( + @JsonProperty("lhs") Expression lhs, + @JsonProperty("rhs") Expression rhs) {} + } + + abstract static class SubtractMixIn extends BinaryExpressionMixIn { + @JsonCreator + SubtractMixIn( + @JsonProperty("lhs") Expression lhs, + @JsonProperty("rhs") Expression rhs) {} + } + + abstract static class MultiplyMixIn extends BinaryExpressionMixIn { + @JsonCreator + MultiplyMixIn( + @JsonProperty("lhs") Expression lhs, + @JsonProperty("rhs") Expression rhs) {} + } + + abstract static class DivideMixIn extends BinaryExpressionMixIn { + @JsonCreator + DivideMixIn( + @JsonProperty("lhs") Expression lhs, + @JsonProperty("rhs") Expression rhs) {} + } + + abstract static class LeftExtentMixIn { + @JsonCreator + LeftExtentMixIn(@JsonProperty("label") String label) {} + } + + abstract static class RightExtentMixIn { + @JsonCreator + RightExtentMixIn(@JsonProperty("label") String label) {} + } + + abstract static class YieldMixIn { + @JsonCreator + YieldMixIn(@JsonProperty("label") String label) {} + } + + abstract static class ValMixIn { + @JsonCreator + ValMixIn(@JsonProperty("label") String label) {} + } + + abstract static class EndOfFileMixIn { + @JsonCreator + EndOfFileMixIn(@JsonProperty("index") Expression index) {} + } + } + + @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "kind") + @JsonSubTypes({ + @JsonSubTypes.Type(value = Statement.Expression.class, name = "ExpressionStatement"), + @JsonSubTypes.Type(value = Statement.VariableDeclaration.class, name = "VariableDeclaration") + }) + abstract static class StatementMixIn { + + abstract static class ExpressionMixIn { + @JsonCreator + ExpressionMixIn(@JsonProperty("exp") Expression value) {} + } + + abstract static class VariableDeclarationMixIn { + @JsonCreator + VariableDeclarationMixIn(@JsonProperty("decl") VariableDeclaration value) {} + } + } + + abstract static class RegularExpressionConditionMixIn { + @JsonCreator + RegularExpressionConditionMixIn( + @JsonProperty("type") ConditionType type, + @JsonProperty("regularExpression") RegularExpression regularExpression) {} + } + + abstract static class DataDependentConditionMixIn { + @JsonCreator + DataDependentConditionMixIn( + @JsonProperty("type") ConditionType type, + @JsonProperty("expression") Expression expression) {} + } + + abstract static class PositionalConditionMixIn { + @JsonCreator + PositionalConditionMixIn(@JsonProperty("type") ConditionType type) {} + } + + abstract static class AbstractAttrsMixIn { + @JsonIgnore + io.usethesource.capsule.Set.Immutable env; + } + + abstract static class ParseErrorMixIn { + @JsonIgnore + GSSNode gssNode; + + @JsonIgnore + GrammarSlot slot; + + ParseErrorMixIn( + @JsonProperty("slot") GrammarSlot slot, + @JsonProperty("gssNode") GSSNode gssNode, + @JsonProperty("inputIndex") int inputIndex, + @JsonProperty("lineNumber") int lineNumber, + @JsonProperty("columnNumber") int columnNumber, + @JsonProperty("description") String description) {} + } + + abstract static class PrecedenceLevelMixIn { + PrecedenceLevelMixIn( + @JsonProperty("lhs") int lhs, + @JsonProperty("rhs") int rhs, + @JsonProperty("undefined") int undefined, + @JsonProperty("hasPrefixUnary") boolean hasPrefixUnary, + @JsonProperty("hasPostfixUnary") boolean hasPostfixUnary, + @JsonProperty("hasPrefixUnaryBelow") boolean hasPrefixUnaryBelow, + @JsonProperty("prefixUnaryBelow") Integer[] prefixUnaryBelow, + @JsonProperty("hasPostfixUnaryBelow") boolean hasPostfixUnaryBelow, + @JsonProperty("postfixUnaryBelow") Integer[] postfixUnaryBelow) {} + } + + abstract static class AssociativityGroupMixIn { + AssociativityGroupMixIn( + @JsonProperty("associativity") Associativity associativity, + @JsonProperty("precedenceLevel") PrecedenceLevel precedenceLevel, + @JsonProperty("lhs") int lhs, + @JsonProperty("rhs") int rhs, + @JsonProperty("precedence") int precedence) {} + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/util/serialization/ParseStatisticsSerializer.java b/benchmarks/src/main/kotlin/org/iguana/util/serialization/ParseStatisticsSerializer.java new file mode 100644 index 000000000..1daac78db --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/util/serialization/ParseStatisticsSerializer.java @@ -0,0 +1,49 @@ +package org.iguana.util.serialization; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.util.DefaultIndenter; +import com.fasterxml.jackson.core.util.DefaultPrettyPrinter; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import org.iguana.parser.ParseStatistics; + +import java.io.IOException; + +public class ParseStatisticsSerializer { + + private static ObjectMapper mapper; + + static { + mapper = new ObjectMapper(); + mapper.setVisibility(mapper.getSerializationConfig().getDefaultVisibilityChecker() + .withFieldVisibility(JsonAutoDetect.Visibility.ANY) + .withGetterVisibility(JsonAutoDetect.Visibility.NONE) + .withIsGetterVisibility(JsonAutoDetect.Visibility.NONE)); + + mapper.addMixIn(ParseStatistics.class, ParseStatisticsMixIn.class); + } + + public static String serialize(ParseStatistics statistics) { + DefaultPrettyPrinter pp = new DefaultPrettyPrinter(); + pp.indentArraysWith(DefaultIndenter.SYSTEM_LINEFEED_INSTANCE); + try { + return mapper.writer(pp).writeValueAsString(statistics); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + } + + public static ParseStatistics deserialize(String s) { + try { + return mapper.readValue(s, ParseStatistics.class); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @JsonDeserialize(builder = ParseStatistics.Builder.class) + abstract static class ParseStatisticsMixIn { + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/util/serialization/RecognizerStatisticsSerializer.java b/benchmarks/src/main/kotlin/org/iguana/util/serialization/RecognizerStatisticsSerializer.java new file mode 100644 index 000000000..5b99f8740 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/util/serialization/RecognizerStatisticsSerializer.java @@ -0,0 +1,53 @@ +package org.iguana.util.serialization; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.util.DefaultIndenter; +import com.fasterxml.jackson.core.util.DefaultPrettyPrinter; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import org.iguana.parser.RecognizerStatistics; + +import java.io.IOException; + +public class RecognizerStatisticsSerializer { + + private static ObjectMapper mapper; + + static { + mapper = new ObjectMapper(); + mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) + .setVisibility(mapper.getSerializationConfig().getDefaultVisibilityChecker() + .withFieldVisibility(JsonAutoDetect.Visibility.ANY) + .withGetterVisibility(JsonAutoDetect.Visibility.NONE) + .withIsGetterVisibility(JsonAutoDetect.Visibility.NONE)); + + mapper.addMixIn(RecognizerStatistics.class, RecognizerStatisticsMixIn.class); + } + + public static String serialize(RecognizerStatistics statistics) { + DefaultPrettyPrinter pp = new DefaultPrettyPrinter(); + pp.indentArraysWith(DefaultIndenter.SYSTEM_LINEFEED_INSTANCE); + try { + return mapper.writer(pp).writeValueAsString(statistics); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + } + + public static RecognizerStatistics deserialize(String s) { + try { + return mapper.readValue(s, RecognizerStatistics.class); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @JsonDeserialize(builder = RecognizerStatistics.Builder.class) + @JsonIgnoreProperties(ignoreUnknown = true) + abstract static class RecognizerStatisticsMixIn { + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/util/trie/Edge.java b/benchmarks/src/main/kotlin/org/iguana/util/trie/Edge.java new file mode 100644 index 000000000..46af2fddd --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/util/trie/Edge.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.util.trie; + + + +public class Edge { + + private T label; + + private Node destination; + + public Edge(T label, Node destination) { + + if (label == null) throw new IllegalArgumentException("Label cannot be null."); + if (destination == null) throw new IllegalArgumentException("Destination cannot be null."); + + this.label = label; + this.destination = destination; + } + + public T getLabel() { + return label; + } + + public Node getDestination() { + return destination; + } + + @Override + public String toString() { + return label.toString(); + } + + @Override + public int hashCode() { + return label.hashCode(); + } + + @Override + public boolean equals(Object obj) { + + if (obj == this) return true; + + if (!(obj instanceof Edge)) return false; + + @SuppressWarnings("unchecked") + Edge other = (Edge) obj; + + return label.equals(other.label); + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/util/trie/ExternalEqual.java b/benchmarks/src/main/kotlin/org/iguana/util/trie/ExternalEqual.java new file mode 100644 index 000000000..cfd3eabd2 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/util/trie/ExternalEqual.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.util.trie; + +public interface ExternalEqual { + + boolean isEqual(T t1, T t2); + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/util/trie/Node.java b/benchmarks/src/main/kotlin/org/iguana/util/trie/Node.java new file mode 100644 index 000000000..69dc9be08 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/util/trie/Node.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.util.trie; + +import java.util.ArrayList; +import java.util.List; + + +public class Node { + + private List> edges; + + public Node() { + edges = new ArrayList<>(); + } + + public int size() { + return edges.size(); + } + + + public List> getEdges() { + return edges; + } + + + public void addChild(Edge edge) { + edges.add(edge); + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/util/trie/Trie.java b/benchmarks/src/main/kotlin/org/iguana/util/trie/Trie.java new file mode 100644 index 000000000..c06a08ea6 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/util/trie/Trie.java @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.util.trie; + +public class Trie { + + private Node root; + + public Trie() { + this.root = new Node<>(); + } + + public void add(T label) { + add(root, label); + } + + public Node get(Iterable prefix) { + Node node = root; + + for (T label : prefix) { + node = getNodeWithEdgeLabel(node, label); + if (node == null) { + return null; + } + } + + return node; + } + + public void addToRoot(Iterable labels) { + Node node = root; + for (T label : labels) { + node = add(node, label); + } + } + + public Node add(Node node, T label) { + if (node.size() == 0) { + return insert(node, label); + } + + Node dest = getNodeWithEdgeLabel(node, label); + if (dest == null) { + return insert(node, label); + } else { + return dest; + } + } + + private Node getNodeWithEdgeLabel(Node node, T label) { + for (Edge edge : node.getEdges()) { + if (edge.getLabel().equals(label)) { + return edge.getDestination(); + } + } + return null; + } + + private Node insert(Node node, T label) { + Node newNode = new Node<>(); + node.addChild(new Edge(label, newNode)); + return newNode; + } + + public Node getRoot() { + return root; + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/util/visualization/GSSToDot.java b/benchmarks/src/main/kotlin/org/iguana/util/visualization/GSSToDot.java new file mode 100644 index 000000000..1b131d802 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/util/visualization/GSSToDot.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.util.visualization; + +import org.iguana.gss.GSSEdge; +import org.iguana.gss.GSSNode; +import org.iguana.utils.visualization.DotGraph; + +import java.util.HashMap; +import java.util.Map; + +import static org.iguana.utils.visualization.DotGraph.newEdge; +import static org.iguana.utils.visualization.DotGraph.newNode; + +public class GSSToDot { + + private final Map, Integer> ids = new HashMap<>(); + + public DotGraph execute(Iterable> set) { + DotGraph dotGraph = new DotGraph(); + + for (GSSNode gssNode : set) { + dotGraph.addNode(newNode(getId(gssNode), gssNode.toString())); + + for (GSSEdge edge : gssNode.getGSSEdges()) { + DotGraph.Edge dotEdge = newEdge(getId(gssNode), getId(edge.getDestination())); + if (edge.getReturnSlot() != null) { + dotEdge.setLabel(edge.getReturnSlot().toString()); + } + dotGraph.addEdge(dotEdge); + } + } + + return dotGraph; + } + + private int getId(GSSNode node) { + return ids.computeIfAbsent(node, k -> ids.size() + 1); + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/util/visualization/GrammarGraphToDot.java b/benchmarks/src/main/kotlin/org/iguana/util/visualization/GrammarGraphToDot.java new file mode 100644 index 000000000..e8d242509 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/util/visualization/GrammarGraphToDot.java @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.util.visualization; + +import org.iguana.grammar.GrammarGraph; +import org.iguana.grammar.slot.BodyGrammarSlot; +import org.iguana.grammar.slot.ConditionalTransition; +import org.iguana.grammar.slot.EndGrammarSlot; +import org.iguana.grammar.slot.GrammarSlot; +import org.iguana.grammar.slot.NonterminalGrammarSlot; +import org.iguana.grammar.slot.Transition; +import org.iguana.utils.visualization.DotGraph; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import static org.iguana.utils.string.StringUtil.listToString; +import static org.iguana.utils.visualization.DotGraph.newEdge; +import static org.iguana.utils.visualization.DotGraph.newNode; + +public class GrammarGraphToDot { + + private static final Map ids = new HashMap<>(); + + public static DotGraph toDot(GrammarGraph g, DotGraph.Direction direction) { + DotGraph dotGraph = new DotGraph(direction); + generate(dotGraph, g); + return dotGraph; + } + + public static DotGraph toDot(GrammarGraph g) { + DotGraph dotGraph = new DotGraph(); + generate(dotGraph, g); + return dotGraph; + } + + private static void generate(DotGraph dotGraph, GrammarGraph g) { + ids.clear(); + Set visited = new HashSet<>(); + for (NonterminalGrammarSlot nonterminal : g.getNonterminalGrammarSlots()) { + toDot(nonterminal, dotGraph, visited); + } + } + + private static void toDot(NonterminalGrammarSlot slot, DotGraph dotGraph, Set visited) { + int slotId = getId(slot); + if (visited.contains(slotId)) { + return; + } + + visited.add(slotId); + DotGraph.Node node = newNode(slotId); + String label; + if (slot.getNonterminal().getParameters() != null) { + label = String.format("%s(%s)", slot.getNonterminal().getName(), + listToString(slot.getNonterminal().getParameters(), ",")); + } else { + label = slot.getNonterminal().getName(); + } + node.setLabel(label); + dotGraph.addNode(node); + + slot.getFirstSlots().forEach(s -> dotGraph.addEdge(newEdge(slotId, getId(s), ""))); + slot.getFirstSlots().forEach(s -> toDot(s, dotGraph)); + } + + private static void toDot(BodyGrammarSlot slot, DotGraph dotGraph) { + if (slot instanceof EndGrammarSlot) { + dotGraph.addNode(newNode(getId(slot), slot.toString()).setShape(DotGraph.Shape.ROUNDED_RECTANGLE)); + } else { + dotGraph.addNode(newNode(getId(slot), slot.toString()).setShape(DotGraph.Shape.ROUNDED_RECTANGLE)); + + // TODO: improve this code + Transition t = slot.getOutTransition(); + if (t instanceof ConditionalTransition) { + dotGraph.addEdge(newEdge(getId(slot), getId(t.destination()), t.getLabel())); + + BodyGrammarSlot ifFalse = ((ConditionalTransition) t).ifFalseDestination(); + if (ifFalse != null) + dotGraph.addEdge(newEdge(getId(slot), getId(ifFalse), t.getLabel())); + } + else { + dotGraph.addEdge(newEdge(getId(slot), getId(t.destination()), t.getLabel())); + } + + toDot(t.destination(), dotGraph); + } + } + + private static int getId(GrammarSlot slot) { + return ids.computeIfAbsent(slot, k -> ids.size() + 1); + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/util/visualization/ParseTreeToDot.java b/benchmarks/src/main/kotlin/org/iguana/util/visualization/ParseTreeToDot.java new file mode 100644 index 000000000..c4521743c --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/util/visualization/ParseTreeToDot.java @@ -0,0 +1,148 @@ +package org.iguana.util.visualization; + +import org.iguana.parsetree.AmbiguityNode; +import org.iguana.parsetree.ErrorNode; +import org.iguana.parsetree.MetaSymbolNode; +import org.iguana.parsetree.NonterminalNode; +import org.iguana.parsetree.ParseTreeNode; +import org.iguana.parsetree.ParseTreeVisitor; +import org.iguana.parsetree.TerminalNode; +import org.iguana.utils.input.Input; +import org.iguana.utils.visualization.DotGraph; + +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.Set; + +import static org.iguana.utils.visualization.DotGraph.newEdge; +import static org.iguana.utils.visualization.DotGraph.newNode; + +public class ParseTreeToDot implements ParseTreeVisitor { + + private final DotGraph dotGraph; + private final Input input; + private final Set exclude; + private int id = 0; + + private ParseTreeToDot(DotGraph dotGraph, Input input, Set exclude) { + this.dotGraph = dotGraph; + this.input = input; + this.exclude = exclude; + } + + public static DotGraph getDotGraph(ParseTreeNode node, Input input) { + return getDotGraph(node, input, Collections.emptySet()); + } + + public static DotGraph getDotGraph(ParseTreeNode node, Input input, Set exclude) { + DotGraph dotGraph = new DotGraph(); + node.accept(new ParseTreeToDot(dotGraph, input, exclude)); + return dotGraph; + } + + @Override + public Integer visitNonterminalNode(NonterminalNode node) { + int id = nextId(); + String label = String.format("(%s, %d, %d)", node.getGrammarDefinition().getHead().getName(), node.getStart(), + node.getEnd()); + dotGraph.addNode(newNode(id, label)); + + visitChildren(node, id); + return id; + } + + @Override + public List visitStarNode(MetaSymbolNode.StarNode node) { + return visitMetaSymbolNode(node); + } + + @Override + public List visitPlusNode(MetaSymbolNode.PlusNode node) { + return visitMetaSymbolNode(node); + } + + @Override + public Optional visitOptionNode(MetaSymbolNode.OptionNode node) { + return Optional.of(visitMetaSymbolNode(node).get(0)); + } + + @Override + public List visitStartNode(MetaSymbolNode.StartNode node) { + return visitMetaSymbolNode(node); + } + + @Override + public Integer visitAltNode(MetaSymbolNode.AltNode node) { + return visitMetaSymbolNode(node).get(0); + } + + @Override + public List visitGroupNode(MetaSymbolNode.GroupNode node) { + return visitMetaSymbolNode(node); + } + + private List visitMetaSymbolNode(MetaSymbolNode node) { + int id = nextId(); + String label = String.format("%s", node.getName()); + dotGraph.addNode(newNode(id, label).setShape(DotGraph.Shape.RECTANGLE)); + + visitChildren(node, id); + return Collections.singletonList(id); + } + + @Override + public List visitAmbiguityNode(AmbiguityNode node) { + int id = nextId(); + dotGraph.addNode(newNode(id).setShape(DotGraph.Shape.DIAMOND).setColor(DotGraph.Color.RED)); + + visitChildren(node, id); + return Collections.singletonList(id); + } + + @Override + public Integer visitTerminalNode(TerminalNode node) { + if (exclude.contains(node.getGrammarDefinition().getName())) return null; + + String text = input.subString(node.getStart(), node.getEnd()); + String label = String.format("(%s, %d, %d): \"%s\"", node.getGrammarDefinition().getName(), node.getStart(), + node.getEnd(), text); + int id = nextId(); + dotGraph.addNode(newNode(id, label).setShape(DotGraph.Shape.ROUNDED_RECTANGLE)); + return id; + } + + @Override + public Integer visitErrorNode(ErrorNode node) { + String text = input.subString(node.getStart(), node.getEnd()); + String label = String.format("(Error, %d, %d): \"%s\"", node.getStart(), node.getEnd(), text); + int id = nextId(); + dotGraph.addNode(newNode(id, label).setShape(DotGraph.Shape.ROUNDED_RECTANGLE)); + return id; + } + + private int nextId() { + return id++; + } + + private void addEdgeToChild(int parentNodeId, int childNodeId) { + dotGraph.addEdge(newEdge(parentNodeId, childNodeId)); + } + + private void visitChildren(ParseTreeNode node, int nodeId) { + for (ParseTreeNode child : node.children()) { + if (child != null) { + Object childId = child.accept(this); + if (childId != null) { + if (childId instanceof List) { + addEdgeToChild(nodeId, ((List) childId).get(0)); + } else if (childId instanceof Optional) { + addEdgeToChild(nodeId, ((Optional) childId).get()); + } else { + addEdgeToChild(nodeId, (Integer) childId); + } + } + } + } + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/util/visualization/SPPFToDot.java b/benchmarks/src/main/kotlin/org/iguana/util/visualization/SPPFToDot.java new file mode 100644 index 000000000..3c4afb75a --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/util/visualization/SPPFToDot.java @@ -0,0 +1,191 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.util.visualization; + +import org.iguana.sppf.ErrorNode; +import org.iguana.sppf.IntermediateNode; +import org.iguana.sppf.NonPackedNode; +import org.iguana.sppf.NonterminalNode; +import org.iguana.sppf.PackedNode; +import org.iguana.sppf.SPPFNode; +import org.iguana.sppf.TerminalNode; +import org.iguana.traversal.SPPFVisitor; +import org.iguana.utils.input.Input; +import org.iguana.utils.visualization.DotGraph; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static org.iguana.utils.string.StringUtil.listToString; +import static org.iguana.utils.visualization.DotGraph.newEdge; +import static org.iguana.utils.visualization.DotGraph.newNode; + +public class SPPFToDot implements SPPFVisitor { + + protected Input input; + + private final boolean showPackedNodeLabel; + + private final Map ids = new HashMap<>(); + + private final DotGraph dotGraph; + + private final Set visited = new HashSet<>(); + + public SPPFToDot(Input input, DotGraph dotGraph, boolean showPackedNodeLabel) { + this.input = input; + this.showPackedNodeLabel = showPackedNodeLabel; + this.dotGraph = dotGraph; + } + + public static DotGraph getDotGraph(SPPFNode root, Input input) { + return getDotGraph(root, input, false); + } + + public static DotGraph getDotGraph(SPPFNode root, Input input, boolean showPackedNodeLabel) { + DotGraph dotGraph = new DotGraph(); + SPPFToDot sppfToDot = new SPPFToDot(input, dotGraph, showPackedNodeLabel); + root.accept(sppfToDot); + return dotGraph; + } + + @Override + public Void visit(TerminalNode node) { + if (!visited.contains(node)) { + visited.add(node); + String matchedInput = input.subString(node.getLeftExtent(), node.getRightExtent()); + String label = String.format("(%s, %d, %d): \"%s\"", node.getGrammarSlot(), node.getLeftExtent(), + node.getRightExtent(), matchedInput); + dotGraph.addNode(newNode(getId(node), label)); + } + + return null; + } + + @Override + public Void visit(NonterminalNode node) { + if (!visited.contains(node)) { + visited.add(node); + + String label; + if (node.getValue() == null) + label = String.format("(%s, %d, %d)", node.getGrammarSlot(), node.getLeftExtent(), + node.getRightExtent()); + else { + if (node.getValue() instanceof List) + label = String.format("(%s, %d, %d, %s)", node.getGrammarSlot(), node.getLeftExtent(), + node.getRightExtent(), + "(" + listToString((List) node.getValue(), ",") + ")"); + else + label = String.format("(%s, %d, %d, %s)", node.getGrammarSlot(), node.getLeftExtent(), + node.getRightExtent(), node.getValue()); + } + + DotGraph.Node dotNode = newNode(getId(node), label); + if (node.isAmbiguous()) { + dotNode.setColor(DotGraph.Color.RED); + } + dotGraph.addNode(dotNode); + addEdgesToChildren(node); + + visitChildren(node); + } + return null; + } + + @Override + public Void visit(IntermediateNode node) { + if (!visited.contains(node)) { + visited.add(node); + + String label = String.format("(%s, %d, %d)", node.getGrammarSlot(), node.getLeftExtent(), + node.getRightExtent()); + + DotGraph.Node dotNode = newNode(getId(node), label).setShape(DotGraph.Shape.RECTANGLE); + if (node.isAmbiguous()) { + dotNode.setColor(DotGraph.Color.RED); + } + dotGraph.addNode(dotNode); + addEdgesToChildren(node); + + visitChildren(node); + } + return null; + } + + @Override + public Void visit(PackedNode node) { + DotGraph.Node dotNode = newNode(getId(node)).setShape(DotGraph.Shape.CIRCLE); + if (showPackedNodeLabel) { + dotNode.setLabel(node.toString()); + } + dotGraph.addNode(dotNode); + + addEdgesToChildren(node); + + visitChildren(node); + return null; + } + + @Override + public Void visit(ErrorNode node) { + if (!visited.contains(node)) { + visited.add(node); + String matchedInput = input.subString(node.getLeftExtent(), node.getRightExtent()); + String label = String.format("(Error, %d, %d): \"%s\"", node.getLeftExtent(), node.getRightExtent(), + matchedInput); + dotGraph.addNode(newNode(getId(node), label)); + } + + return null; + } + + private void addEdgesToChildren(SPPFNode node) { + for (int i = 0; i < node.childrenCount(); i++) { + addEdgeToChild(node, node.getChildAt(i)); + } + } + + private void addEdgeToChild(SPPFNode parentNode, SPPFNode childNode) { + dotGraph.addEdge(newEdge(getId(parentNode), getId(childNode))); + } + + private void visitChildren(SPPFNode node) { + for (int i = 0; i < node.childrenCount(); i++) { + node.getChildAt(i).accept(this); + } + } + + private int getId(SPPFNode node) { + return ids.computeIfAbsent(node, k -> ids.size() + 1); + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/utils/Assert.java b/benchmarks/src/main/kotlin/org/iguana/utils/Assert.java new file mode 100644 index 000000000..209d51192 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/utils/Assert.java @@ -0,0 +1,18 @@ +package org.iguana.utils; + +import java.util.Collection; + +public class Assert { + + public static int requireNonNegative(int number) { + if (number < 0) + throw new IllegalArgumentException(number + " is negative"); + return number; + } + + public static Collection requireNonEmpty(Collection collection) { + if (collection.isEmpty()) + throw new IllegalArgumentException("collection is empty"); + return collection; + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/utils/benchmark/BenchmarkUtil.java b/benchmarks/src/main/kotlin/org/iguana/utils/benchmark/BenchmarkUtil.java new file mode 100644 index 000000000..a69429459 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/utils/benchmark/BenchmarkUtil.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.utils.benchmark; + +import com.google.common.testing.GcFinalization; + +import java.lang.management.ManagementFactory; +import java.lang.management.ThreadMXBean; + +public class BenchmarkUtil { + + public static int getMemoryUsed() { + int mb = 1024 * 1024; + Runtime runtime = Runtime.getRuntime(); + return (int) ((runtime.totalMemory() - runtime.freeMemory()) / mb); + } + + public static long getUserTime() { + ThreadMXBean bean = ManagementFactory.getThreadMXBean(); + return bean.isCurrentThreadCpuTimeSupported() ? bean.getCurrentThreadUserTime() : 0L; + } + + public static long getSystemTime() { + ThreadMXBean bean = ManagementFactory.getThreadMXBean(); + return bean.isCurrentThreadCpuTimeSupported() + ? (bean.getCurrentThreadCpuTime() - bean.getCurrentThreadUserTime()) + : 0L; + } + + public static void awaitFullGC() { + GcFinalization.awaitFullGc(); + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/utils/benchmark/Timer.java b/benchmarks/src/main/kotlin/org/iguana/utils/benchmark/Timer.java new file mode 100644 index 000000000..dfa2257f0 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/utils/benchmark/Timer.java @@ -0,0 +1,53 @@ +package org.iguana.utils.benchmark; + +public class Timer { + + private long startNanoTime; + private long startUserTime; + private long startSystemTime; + + private long endNanoTime; + private long endUserTime; + private long endSystemTime; + + private boolean running; + + public void start() { + if (running) { + throw new RuntimeException("The timer is already running. Reset first before start!"); + } + running = true; + startNanoTime = System.nanoTime(); + startSystemTime = BenchmarkUtil.getSystemTime(); + startUserTime = BenchmarkUtil.getUserTime(); + } + + public void stop() { + endNanoTime = System.nanoTime(); + endSystemTime = BenchmarkUtil.getSystemTime(); + endUserTime = BenchmarkUtil.getUserTime(); + } + + public void reset() { + running = false; + startNanoTime = 0; + startUserTime = 0; + startSystemTime = 0; + endNanoTime = 0; + endUserTime = 0; + endSystemTime = 0; + } + + public long getNanoTime() { + return endNanoTime - startNanoTime; + } + + public long getUserTime() { + return endUserTime - startUserTime; + } + + public long getSystemTime() { + return endSystemTime - startSystemTime; + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/utils/collections/CollectionsUtil.java b/benchmarks/src/main/kotlin/org/iguana/utils/collections/CollectionsUtil.java new file mode 100644 index 000000000..a88525f44 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/utils/collections/CollectionsUtil.java @@ -0,0 +1,260 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.utils.collections; + +import org.iguana.util.Tuple; +import org.iguana.utils.collections.primitive.IntIterator; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.BiFunction; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import java.util.stream.Stream.Builder; +import java.util.stream.StreamSupport; + +public class CollectionsUtil { + + @SafeVarargs + @SuppressWarnings("varargs") + public static Set set(T... objects) { + if (objects.length == 0) return Collections.emptySet(); + + Set set = new HashSet<>(); + Collections.addAll(set, objects); + return set; + } + + @SafeVarargs + @SuppressWarnings("varargs") + public static Set immutableSet(T... objects) { + return Collections.unmodifiableSet(set(objects)); + } + + @SafeVarargs + @SuppressWarnings("varargs") + public static List list(T... objects) { + if (objects.length == 0) return Collections.emptyList(); + return Arrays.asList(objects); + } + + @SafeVarargs + @SuppressWarnings("varargs") + public static List immutableList(T... objects) { + return Collections.unmodifiableList(list(objects)); + } + + public static Tuple tuple(K k, V v) { + return Tuple.of(k, v); + } + + public static Map map(List> mappings) { + return mappings.stream().collect(Collectors.toMap(t -> t.getFirst(), t -> t.getSecond())); + } + + public static Map map(List keys, List values) { + return zip(keys.stream(), values.stream(), (k, v) -> tuple(k, v)).collect( + Collectors.toMap(t -> t.getFirst(), t -> t.getSecond())); + } + + public static Stream zip(Stream as, Stream bs, BiFunction f) { + Iterator asIterator = as.iterator(); + Iterator bsIterator = bs.iterator(); + + Builder builder = Stream.builder(); + + while (asIterator.hasNext() && bsIterator.hasNext()) { + builder.add(f.apply(asIterator.next(), bsIterator.next())); + } + + return builder.build(); + } + + public static Set union(Set set1, Set set2) { + Set set = new HashSet<>(); + set.addAll(set1); + set.addAll(set2); + return set; + } + + public static List flatten(List> listOfList) { + List result = new ArrayList<>(); + for (List list : listOfList) { + result.addAll(list); + } + return result; + } + + public static T first(List list) { + if (list == null || list.isEmpty()) + throw new IllegalArgumentException("List is empty"); + return list.get(0); + } + + public static T last(List list) { + if (list == null || list.isEmpty()) + throw new IllegalArgumentException("List is empty"); + return list.get(list.size() - 1); + } + + public static boolean isEqual(IntIterator it1, IntIterator it2) { + while (it1.hasNext()) { + if (!it2.hasNext()) return false; + int t1 = it1.next(); + int t2 = it2.next(); + if (t1 != t2) return false; + } + return true; + } + + public static boolean isEqual(Iterable iterables1, Iterable iterables2) { + Iterator it1 = iterables1.iterator(); + Iterator it2 = iterables2.iterator(); + while (it1.hasNext()) { + if (!it2.hasNext()) return false; + T t1 = it1.next(); + T t2 = it2.next(); + if (!t1.equals(t2)) return false; + } + return true; + } + + public static Iterable concat(Iterable first, Iterable second) { + + return new Iterable() { + + final Iterator it1 = first.iterator(); + final Iterator it2 = second.iterator(); + + @Override + public Iterator iterator() { + + return new Iterator<>() { + + Iterator curr; + + @Override + public boolean hasNext() { + if (it1.hasNext()) { + curr = it1; + return true; + } else if (it2.hasNext()) { + curr = it2; + return true; + } + return false; + } + + @Override + public T next() { + return curr.next(); + } + }; + } + }; + } + + public static List> split(List list, int size) { + List> result = new ArrayList<>(); + + int n = list.size() / size; + int r = list.size() % size; + + for (int i = 0; i < n; i++) { + List split = new ArrayList<>(); + for (int j = i * size; j < i * size + size; j++) { + split.add(list.get(j)); + } + result.add(split); + } + + if (r > 0) { + List rest = new ArrayList<>(); + for (int i = n * size; i < list.size(); i++) { + rest.add(list.get(i)); + } + result.add(rest); + } + + return result; + } + + public static List toList(Iterable it) { + return StreamSupport.stream(it.spliterator(), false).collect(Collectors.toList()); + } + + public static int max(int[] a) { + int max = Integer.MIN_VALUE; + for (int i = 0; i < a.length; i++) + if (a[i] > max) + max = a[i]; + return max; + } + + public static List concat(List list1, List list2) { + List list = new ArrayList<>(list1.size() + list2.size()); + list.addAll(list1); + list.addAll(list2); + return buildList(list); + } + + public static List buildList(List list) { + if (list == null) throw new NullPointerException("List cannot be null."); + if (list.isEmpty()) return Collections.emptyList(); + if (list.size() == 1) return Collections.singletonList(list.get(0)); + if (list.size() == 2) return List.of(list.get(0), list.get(1)); + return list; + } + + public static Set buildSet(Set set) { + if (set == null) throw new NullPointerException("Set cannot be null."); + if (set.isEmpty()) return Collections.emptySet(); + if (set.size() == 1) return Collections.singleton(set.iterator().next()); + if (set.size() == 2) { + Iterator it = set.iterator(); + return Set.of(it.next(), it.next()); + } + return set; + } + + public static Map buildMap(Map map) { + if (map == null) throw new NullPointerException("Map cannot be null."); + if (map.isEmpty()) return Collections.emptyMap(); + if (map.size() == 1) { + Map.Entry elem = map.entrySet().iterator().next(); + return Collections.singletonMap(elem.getKey(), elem.getValue()); + } + return map; + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/utils/collections/HashMapFactory.java b/benchmarks/src/main/kotlin/org/iguana/utils/collections/HashMapFactory.java new file mode 100644 index 000000000..5f7b6bb35 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/utils/collections/HashMapFactory.java @@ -0,0 +1,5 @@ +package org.iguana.utils.collections; + +public interface HashMapFactory { + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/utils/collections/IntKeyMapper.java b/benchmarks/src/main/kotlin/org/iguana/utils/collections/IntKeyMapper.java new file mode 100644 index 000000000..200e5fbf0 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/utils/collections/IntKeyMapper.java @@ -0,0 +1,6 @@ +package org.iguana.utils.collections; + +@FunctionalInterface +public interface IntKeyMapper { + T apply(int k, T v); +} diff --git a/benchmarks/src/main/kotlin/org/iguana/utils/collections/IntUtils.java b/benchmarks/src/main/kotlin/org/iguana/utils/collections/IntUtils.java new file mode 100644 index 000000000..63099858a --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/utils/collections/IntUtils.java @@ -0,0 +1,16 @@ +package org.iguana.utils.collections; + +public class IntUtils { + + public static long merge(int high, int low) { + return ((long) high << 32) | low; + } + + public static int high(long value) { + return (int) (value >>> 32); + } + + public static int low(long value) { + return (int) value; + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/utils/collections/Keys.java b/benchmarks/src/main/kotlin/org/iguana/utils/collections/Keys.java new file mode 100644 index 000000000..0d7f76485 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/utils/collections/Keys.java @@ -0,0 +1,81 @@ +package org.iguana.utils.collections; + +import org.iguana.utils.collections.hash.MurmurHash3; +import org.iguana.utils.collections.key.IntArrayKey; +import org.iguana.utils.collections.key.IntIntIntObjectKey; +import org.iguana.utils.collections.key.IntIntObjectKey; +import org.iguana.utils.collections.key.IntKey1; +import org.iguana.utils.collections.key.IntKey3; +import org.iguana.utils.collections.key.IntKey4; +import org.iguana.utils.collections.key.IntObjectKey; +import org.iguana.utils.collections.key.Key; +import org.iguana.utils.collections.key.ObjectKey2; +import org.iguana.utils.collections.key.ObjectKey3; +import org.iguana.utils.collections.key.ObjectKey4; +import org.iguana.utils.collections.key.ObjectKeyN; +import org.iguana.utils.function.IntFunction3; +import org.iguana.utils.function.IntFunction4; + +public class Keys { + + public static Key from(int a, int b) { + return from(a, b); + } + + public static Key from(int a) { + return new IntKey1(a); + } + + public static Key from(int a, Object o) { + return new IntObjectKey(a, o); + } + + public static Key from(int a, Object[] objects) { + return new IntArrayKey(a, objects); + } + + public static Key from(int a, int b, Object object) { + return new IntIntObjectKey(a, b, object); + } + + public static Key from(int a, int b, int c, Object object) { + return new IntIntIntObjectKey(a, b, c, object); + } + + public static Key from(Object a, Object b) { + return new ObjectKey2(a, b, MurmurHash3.f2().apply(a, b)); + } + + public static Key from(int a, int b, int c) { + return from(MurmurHash3.f3(), a, b, c); + } + + public static Key from(IntFunction3 f, int a, int b, int c) { + return new IntKey3(a, b, c, f.apply(a, b, c)); + } + + public static Key from(Object a, Object b, Object c) { + return from(MurmurHash3.f3(), a, b, c); + } + + public static Key from(IntFunction3 f, Object a, Object b, Object c) { + return new ObjectKey3(a, b, c, f.apply(a, b, c)); + } + + public static Key from(int a, int b, int c, int d) { + return from(MurmurHash3.f4(), a, b, c, d); + } + + public static Key from(org.iguana.utils.function.IntFunction4 f, int a, int b, int c, int d) { + return new IntKey4(a, b, c, d, f.apply(a, b, c, d)); + } + + public static Key from(IntFunction4 f, Object a, Object b, Object c, Object d) { + return new ObjectKey4(a, b, c, d, f.apply(a, b, c, d)); + } + + public static Key from(Object... elements) { + return new ObjectKeyN(MurmurHash3.fn(), elements); + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/utils/collections/OpenAddressingHashMap.java b/benchmarks/src/main/kotlin/org/iguana/utils/collections/OpenAddressingHashMap.java new file mode 100644 index 000000000..fe9824cc6 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/utils/collections/OpenAddressingHashMap.java @@ -0,0 +1,240 @@ +package org.iguana.utils.collections; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +public class OpenAddressingHashMap implements Map { + + private static final int DEFAULT_INITIAL_CAPACITY = 16; + private static final float DEFAULT_LOAD_FACTOR = 0.7f; + + private final int initialCapacity; + private final float loadFactor; + + private int capacity; + + private int size; + + private int threshold; + + /** + * capacity - 1 + * The bitMask is used to adj the p most-significant bytes of the multiplicaiton. + */ + private int bitMask; + + private K[] keys; + + private T[] values; + + public OpenAddressingHashMap() { + this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR); + } + + public OpenAddressingHashMap(int initalCapacity) { + this(initalCapacity, DEFAULT_LOAD_FACTOR); + } + + public OpenAddressingHashMap(int initialCapacity, float loadFactor) { + this.initialCapacity = initialCapacity < 0 ? DEFAULT_INITIAL_CAPACITY : initialCapacity; + this.loadFactor = (loadFactor < 0 || loadFactor > 1) ? DEFAULT_LOAD_FACTOR : loadFactor; + init(); + } + + @SuppressWarnings("unchecked") + private void init() { + capacity = 1; + while (capacity < initialCapacity) capacity <<= 1; + + bitMask = capacity - 1; + + threshold = (int) (loadFactor * capacity); + keys = (K[]) new Object[capacity]; + + values = (T[]) new Object[capacity]; + + size = 0; + } + + @Override + public T put(K key, T value) { + int j = 0; + int index = hash(key, j); + + do { + if (keys[index] == null) { + keys[index] = key; + values[index] = value; + size++; + if (size >= threshold) { + rehash(); + } + return null; + } else if (keys[index].equals(key)) { + T oldValue = values[index]; + values[index] = value; + return oldValue; + } + + index = hash(key, ++j); + + } while (true); + } + + @Override + public T remove(Object key) { + return null; + } + + @Override + public void putAll(Map m) { + + } + + @SuppressWarnings("unchecked") + private void rehash() { + capacity <<= 1; + bitMask = capacity - 1; + + K[] newKeys = (K[]) new Object[capacity]; + T[] newValues = (T[]) new Object[capacity]; + + label: + for (int i = 0; i < keys.length; i++) { + int j = 0; + K key = keys[i]; + + T value = values[i]; + + if (key != null) { + + int index = hash(key, j); + + do { + if (newKeys[index] == null) { + newKeys[index] = key; + newValues[index] = value; + continue label; + } + + index = hash(key, ++j); + + } while (true); + } + } + + keys = newKeys; + values = newValues; + threshold = (int) (loadFactor * capacity); + } + + public T get(Object key) { + int j = 0; + int index = hash(key, j); + while (keys[index] != null && !keys[index].equals(key)) { + index = hash(key, ++j); + } + return values[index]; + } + + public int size() { + return size; + } + + public int getInitialCapacity() { + return initialCapacity; + } + + public boolean isEmpty() { + return size == 0; + } + + @Override + public boolean containsKey(Object key) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean containsValue(Object value) { + throw new UnsupportedOperationException(); + } + + public void clear() { + init(); + } + + public String toString() { + if (isEmpty()) return "{ }"; + + StringBuilder sb = new StringBuilder(); + sb.append("{"); + + + for (Entry entry : entrySet()) { + if (entry.getKey() != null) { + sb.append("(" + entry.getKey() + ", " + entry.getValue() + ")"); + sb.append(", "); + } + } + + sb.delete(sb.length() - 2, sb.length()); + sb.append("}"); + + return sb.toString(); + } + + private int hash(Object key, int j) { + return (key.hashCode() + j) & bitMask; + } + + @Override + public Set keySet() { + Set keySet = new HashSet<>(); + for (int i = 0; i < keys.length; i++) { + if (keys[i] != null) keySet.add(keys[i]); + } + return keySet; + } + + @Override + public Collection values() { + Set values = new HashSet<>(size); + + for (int i = 0; i < this.values.length; i++) { + if (this.values[i] != null) values.add(this.values[i]); + } + + return values; + } + + @Override + public Set> entrySet() { + Set> entrySet = new HashSet<>(); + + for (int i = 0; i < keys.length; i++) { + final K key = keys[i]; + final T value = values[i]; + + entrySet.add(new Entry() { + @Override + public K getKey() { + return key; + } + + @Override + public T getValue() { + return value; + } + + @Override + public T setValue(T value) { + return null; + } + }); + } + + return entrySet; + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/utils/collections/hash/MurmurHash2.java b/benchmarks/src/main/kotlin/org/iguana/utils/collections/hash/MurmurHash2.java new file mode 100644 index 000000000..26e7dbcb5 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/utils/collections/hash/MurmurHash2.java @@ -0,0 +1,217 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.utils.collections.hash; + +import org.iguana.utils.function.IntFunction5; +import org.iguana.utils.function.IntFunctionAny; + +public class MurmurHash2 { + + private static final int m = 0x5bd1e995; + private static final int r = 24; + + public static IntFunction5 hash5() { + return hash5(19); + } + + public static IntFunction5 hash5(int seed) { + return (a, b, c, d, e) -> { + + int h = seed ^ 4; + + // a + int k = a; + k = mixK(k); + h = mixH(h, k); + + // b + k = b; + k = mixK(k); + h = mixH(h, k); + + // c + k = c; + k = mixK(k); + h = mixH(h, k); + + // d + k = d; + k = mixK(k); + h = mixH(h, k); + + // last mix + h *= m; + h ^= h >>> 13; + h *= m; + h ^= h >>> 15; + + return h; + }; + } + + public static org.iguana.utils.function.IntFunction4 hash4() { + return hash4(19); + } + + public static org.iguana.utils.function.IntFunction4 hash4(int seed) { + return (a, b, c, d) -> { + + int h = seed ^ 4; + + // a + int k = a; + k = mixK(k); + h = mixH(h, k); + + // b + k = b; + k = mixK(k); + h = mixH(h, k); + + // c + k = c; + k = mixK(k); + h = mixH(h, k); + + // d + k = d; + k = mixK(k); + h = mixH(h, k); + + // last mix + h *= m; + h ^= h >>> 13; + h *= m; + h ^= h >>> 15; + + return h; + }; + } + + public static org.iguana.utils.function.IntFunction3 hash3() { + return hash3(19); + } + + public static org.iguana.utils.function.IntFunction3 hash3(int seed) { + return (a, b, c) -> { + + int h = seed ^ 4; + + // a + int k = a; + k = mixK(k); + h = mixH(h, k); + + // b + k = b; + k = mixK(k); + h = mixH(h, k); + + // c + k = c; + k = mixK(k); + h = mixH(h, k); + + // last mix + h *= m; + h ^= h >>> 13; + h *= m; + h ^= h >>> 15; + + return h; + }; + } + + public static org.iguana.utils.function.IntFunction2 hash2() { + return hash2(19); + } + + public static org.iguana.utils.function.IntFunction2 hash2(int seed) { + return (a, b) -> { + + int h = seed ^ 4; + + // a + int k = a; + k = mixK(k); + h = mixH(h, k); + + // b + k = b; + k = mixK(k); + h = mixH(h, k); + + // last mix + h *= m; + h ^= h >>> 13; + h *= m; + h ^= h >>> 15; + + return h; + }; + } + + public static org.iguana.utils.function.IntFunctionAny hashN() { + return hashN(19); + } + + public static IntFunctionAny hashN(int seed) { + return (Integer... elements) -> { + + int h = seed ^ 4; + + for (int element : elements) { + int k = element; + k = mixK(k); + h = mixH(h, k); + } + + // last mix + h *= m; + h ^= h >>> 13; + h *= m; + h ^= h >>> 15; + + return h; + }; + } + + private static int mixK(int k) { + k *= m; + k ^= k >>> r; + k *= m; + return k; + } + + private static int mixH(int h, int k) { + h *= m; + h ^= k; + return h; + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/utils/collections/hash/MurmurHash3.java b/benchmarks/src/main/kotlin/org/iguana/utils/collections/hash/MurmurHash3.java new file mode 100644 index 000000000..ccd652d41 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/utils/collections/hash/MurmurHash3.java @@ -0,0 +1,223 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.utils.collections.hash; + +import org.iguana.utils.function.IntFunction2; +import org.iguana.utils.function.IntFunction3; +import org.iguana.utils.function.IntFunction4; +import org.iguana.utils.function.IntFunction5; +import org.iguana.utils.function.IntFunctionAny; + +public class MurmurHash3 { + + private static final int C1 = 0xcc9e2d51; + private static final int C2 = 0x1b873593; + private static final int M = 5; + private static final int N = 0xe6546b64; + + public static IntFunction2 f2() { + return f2(19); + } + + public static IntFunction2 f2(int seed) { + return (a, b) -> { + int h = seed; + + int k = a; + k = mixK(k); + h = mixH(h, k); + + k = b; + k = mixK(k); + h = mixH(h, k); + + // finalizing + h ^= 2; + + h ^= h >>> 16; + h *= 0x85ebca6b; + h ^= h >>> 13; + h *= 0xc2b2ae35; + h ^= h >>> 16; + + return h; + }; + } + + public static IntFunction3 f3() { + return f3(19); + } + + public static IntFunction3 f3(int seed) { + return (a, b, c) -> { + int h = seed; + + int k = a; + k = mixK(k); + h = mixH(h, k); + + k = b; + k = mixK(k); + h = mixH(h, k); + + k = c; + k = mixK(k); + h = mixH(h, k); + + // finalizing + h ^= 3; + + h ^= h >>> 16; + h *= 0x85ebca6b; + h ^= h >>> 13; + h *= 0xc2b2ae35; + h ^= h >>> 16; + + return h; + }; + } + + public static IntFunction4 f4() { + return f4(19); + } + + public static IntFunction4 f4(int seed) { + return (a, b, c, d) -> { + int h = seed; + + int k = a; + k = mixK(k); + h = mixH(h, k); + + k = b; + k = mixK(k); + h = mixH(h, k); + + k = c; + k = mixK(k); + h = mixH(h, k); + + k = d; + k = mixK(k); + h = mixH(h, k); + + // finalizing + h ^= 4; + + h ^= h >>> 16; + h *= 0x85ebca6b; + h ^= h >>> 13; + h *= 0xc2b2ae35; + h ^= h >>> 16; + + return h; + }; + } + + public static IntFunction5 f5() { + return f5(19); + } + + public static IntFunction5 f5(int seed) { + return (a, b, c, d, e) -> { + int h = seed; + + int k = a; + k = mixK(k); + h = mixH(h, k); + + k = b; + k = mixK(k); + h = mixH(h, k); + + k = c; + k = mixK(k); + h = mixH(h, k); + + k = d; + k = mixK(k); + h = mixH(h, k); + + k = e; + k = mixK(k); + h = mixH(h, k); + + // finalizing + h ^= 5; + + h ^= h >>> 16; + h *= 0x85ebca6b; + h ^= h >>> 13; + h *= 0xc2b2ae35; + h ^= h >>> 16; + + return h; + }; + } + + public static IntFunctionAny fn() { + return fn(19); + } + + public static IntFunctionAny fn(int seed) { + return (Integer... elements) -> { + int h = seed; + + int k = 0; + for (int element : elements) { + k = element; + k = mixK(k); + h = mixH(h, k); + } + + h ^= elements.length; + + h ^= h >>> 16; + h *= 0x85ebca6b; + h ^= h >>> 13; + h *= 0xc2b2ae35; + h ^= h >>> 16; + + return h; + }; + } + + private static int mixK(int k) { + k *= C1; + k = Integer.rotateLeft(k, 15); + k = k * C2; + return k; + } + + private static int mixH(int h, int k) { + h ^= k; + h = Integer.rotateLeft(h, 13); + h = h * M + N; + return h; + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/utils/collections/hash/PrimeMultiplication.java b/benchmarks/src/main/kotlin/org/iguana/utils/collections/hash/PrimeMultiplication.java new file mode 100644 index 000000000..b738b2579 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/utils/collections/hash/PrimeMultiplication.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.utils.collections.hash; + +import org.iguana.utils.function.IntFunction2; +import org.iguana.utils.function.IntFunction3; +import org.iguana.utils.function.IntFunction4; +import org.iguana.utils.function.IntFunction5; +import org.iguana.utils.function.IntFunctionAny; + +public class PrimeMultiplication { + + private static final int P1 = 17; + private static final int P2 = 31; + + public static IntFunction2 f2() { + return (k1, k2) -> { + int result = P1; + result = P2 * result + k1; + result = P2 * result + k2; + return result; + }; + } + + public static IntFunction3 f3() { + return (k1, k2, k3) -> { + int result = P1; + result = P2 * result + k1; + result = P2 * result + k2; + result = P2 * result + k3; + return result; + }; + } + + public static IntFunction4 f4() { + return (k1, k2, k3, k4) -> { + int result = P1; + result = P2 * result + k1; + result = P2 * result + k2; + result = P2 * result + k3; + result = P2 * result + k4; + return result; + }; + } + + public static IntFunction5 f5() { + return (k1, k2, k3, k4, k5) -> { + int result = P1; + result = P2 * result + k1; + result = P2 * result + k2; + result = P2 * result + k3; + result = P2 * result + k4; + result = P2 * result + k5; + return result; + }; + } + + public static IntFunctionAny fn() { + return (Integer... elements) -> { + int result = P1; + for (int k : elements) { + result = P2 * result + k; + } + return result; + }; + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/utils/collections/key/IntArrayKey.java b/benchmarks/src/main/kotlin/org/iguana/utils/collections/key/IntArrayKey.java new file mode 100644 index 000000000..2c0f75318 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/utils/collections/key/IntArrayKey.java @@ -0,0 +1,33 @@ +package org.iguana.utils.collections.key; + +import java.util.Arrays; + +public class IntArrayKey implements Key { + + private final int i; + private final Object[] objects; + + public IntArrayKey(int i, Object[] objects) { + this.i = i; + this.objects = objects; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (!(obj instanceof IntArrayKey)) return false; + + IntArrayKey other = (IntArrayKey) obj; + return i == other.i && Arrays.deepEquals(objects, other.objects); + } + + @Override + public String toString() { + return String.format("(%d, %s)", i, Arrays.deepToString(objects)); + } + + @Override + public int hashCode() { + return i * 31 + Arrays.deepHashCode(objects); + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/utils/collections/key/IntIntIntObjectKey.java b/benchmarks/src/main/kotlin/org/iguana/utils/collections/key/IntIntIntObjectKey.java new file mode 100644 index 000000000..e2b30a45b --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/utils/collections/key/IntIntIntObjectKey.java @@ -0,0 +1,34 @@ +package org.iguana.utils.collections.key; + +public class IntIntIntObjectKey implements Key { + + private final int i1; + private final int i2; + private final int i3; + private final Object object; + + public IntIntIntObjectKey(int i1, int i2, int i3, Object object) { + this.i1 = i1; + this.i2 = i2; + this.i3 = i3; + this.object = object; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof IntIntIntObjectKey)) return false; + + IntIntIntObjectKey that = (IntIntIntObjectKey) o; + return i1 == that.i1 && i2 == that.i2 && i3 == that.i3 && object.equals(that.object); + } + + @Override + public int hashCode() { + int hash = i1; + hash *= 31 + i2; + hash *= 31 + i3; + hash *= 31 + object.hashCode(); + return hash; + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/utils/collections/key/IntIntObjectKey.java b/benchmarks/src/main/kotlin/org/iguana/utils/collections/key/IntIntObjectKey.java new file mode 100644 index 000000000..02759ac98 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/utils/collections/key/IntIntObjectKey.java @@ -0,0 +1,31 @@ +package org.iguana.utils.collections.key; + +public class IntIntObjectKey implements Key { + + private final int i1; + private final int i2; + private final Object object; + + public IntIntObjectKey(int i1, int i2, Object object) { + this.i1 = i1; + this.i2 = i2; + this.object = object; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof IntIntObjectKey)) return false; + + IntIntObjectKey that = (IntIntObjectKey) o; + return i1 == that.i1 && i2 == that.i2 && object.equals(that.object); + } + + @Override + public int hashCode() { + int hash = i1; + hash *= 31 + i2; + hash *= 31 + object.hashCode(); + return hash; + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/utils/collections/key/IntKey1.java b/benchmarks/src/main/kotlin/org/iguana/utils/collections/key/IntKey1.java new file mode 100644 index 000000000..85a024598 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/utils/collections/key/IntKey1.java @@ -0,0 +1,33 @@ +package org.iguana.utils.collections.key; + +public class IntKey1 implements Key, Comparable { + + private final int k; + + public IntKey1(int k) { + this.k = k; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + + if (!(obj instanceof IntKey1)) + return false; + + IntKey1 other = (IntKey1) obj; + return k == other.k; + } + + @Override + public int hashCode() { + return k; + } + + + @Override + public int compareTo(IntKey1 other) { + return k - other.k; + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/utils/collections/key/IntKey2.java b/benchmarks/src/main/kotlin/org/iguana/utils/collections/key/IntKey2.java new file mode 100644 index 000000000..e63d0177d --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/utils/collections/key/IntKey2.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.utils.collections.key; + +public class IntKey2 implements Key, Comparable { + + private final int k1; + private final int k2; + + public IntKey2(int k1, int k2) { + this.k1 = k1; + this.k2 = k2; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + + if (!(obj instanceof IntKey2)) + return false; + + IntKey2 other = (IntKey2) obj; + return k1 == other.k1 && k2 == other.k2; + } + + @Override + public int hashCode() { + return k1 * 31 + k2; + } + + @Override + public int compareTo(IntKey2 o) { + int r; + return (r = k1 - o.k1) != 0 ? r : k2 - o.k2; + } + + @Override + public String toString() { + return String.format("(%d, %d)", k1, k2); + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/utils/collections/key/IntKey3.java b/benchmarks/src/main/kotlin/org/iguana/utils/collections/key/IntKey3.java new file mode 100644 index 000000000..11a4f56a0 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/utils/collections/key/IntKey3.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.utils.collections.key; + + +public class IntKey3 implements Key, Comparable { + + private final int k1; + private final int k2; + private final int k3; + private final int hash; + + public IntKey3(int k1, int k2, int k3, int hash) { + this.k1 = k1; + this.k2 = k2; + this.k3 = k3; + this.hash = hash; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + + if (!(obj instanceof IntKey3)) + return false; + + IntKey3 other = (IntKey3) obj; + return k1 == other.k1 && k2 == other.k2 && k3 == other.k3; + } + + @Override + public int hashCode() { + return hash; + } + + @Override + public int compareTo(IntKey3 o) { + int r; + return (r = k1 - o.k1) != 0 ? r : (r = k2 - o.k2) != 0 ? r : k3 - o.k3; + } + + @Override + public String toString() { + return String.format("(%d, %d, %d)", k1, k2, k3); + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/utils/collections/key/IntKey4.java b/benchmarks/src/main/kotlin/org/iguana/utils/collections/key/IntKey4.java new file mode 100644 index 000000000..d4669c128 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/utils/collections/key/IntKey4.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.utils.collections.key; + + +public class IntKey4 implements Key, Comparable { + + private final int k1; + private final int k2; + private final int k3; + private final int k4; + + private final int hash; + + public IntKey4(int k1, int k2, int k3, int k4, int hash) { + this.k1 = k1; + this.k2 = k2; + this.k3 = k3; + this.k4 = k4; + this.hash = hash; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + + if (!(obj instanceof IntKey4)) return false; + + IntKey4 other = (IntKey4) obj; + return k1 == other.k1 + && k2 == other.k2 + && k3 == other.k3 + && k4 == other.k4; + } + + @Override + public int hashCode() { + return hash; + } + + @Override + public int compareTo(IntKey4 o) { + int r; + return (r = k1 - o.k1) != 0 ? r + : (r = k2 - o.k2) != 0 ? r + : (r = k3 - o.k3) != 0 ? r + : k4 - o.k4; + } + + @Override + public String toString() { + return String.format("(%d, %d, %d, %d)", k1, k2, k3, k4); + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/utils/collections/key/IntObjectKey.java b/benchmarks/src/main/kotlin/org/iguana/utils/collections/key/IntObjectKey.java new file mode 100644 index 000000000..e2a9b8fb3 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/utils/collections/key/IntObjectKey.java @@ -0,0 +1,26 @@ +package org.iguana.utils.collections.key; + +public class IntObjectKey implements Key { + + private final int i; + private final Object o; + + public IntObjectKey(int i, Object o) { + this.i = i; + this.o = o; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (!(obj instanceof IntObjectKey)) return false; + + IntObjectKey other = (IntObjectKey) obj; + return i == other.i && o.equals(other.o); + } + + @Override + public int hashCode() { + return i * 31 + o.hashCode(); + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/utils/collections/key/Key.java b/benchmarks/src/main/kotlin/org/iguana/utils/collections/key/Key.java new file mode 100644 index 000000000..7b5febc2f --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/utils/collections/key/Key.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.utils.collections.key; + +public interface Key { +} diff --git a/benchmarks/src/main/kotlin/org/iguana/utils/collections/key/ObjectKey2.java b/benchmarks/src/main/kotlin/org/iguana/utils/collections/key/ObjectKey2.java new file mode 100644 index 000000000..6b0d0f84c --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/utils/collections/key/ObjectKey2.java @@ -0,0 +1,36 @@ +package org.iguana.utils.collections.key; + +public class ObjectKey2 implements Key { + + private final Object o1; + private final Object o2; + private final int hash; + + public ObjectKey2(Object o1, Object o2, int hash) { + this.o1 = o1; + this.o2 = o2; + this.hash = hash; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + + if (!(o instanceof ObjectKey2)) return false; + + ObjectKey2 that = (ObjectKey2) o; + return hash == that.hash + && o1.equals(that.o1) + && o2.equals(that.o2); + } + + @Override + public int hashCode() { + return hash; + } + + @Override + public String toString() { + return String.format("(%s, %s)", o1, o2); + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/utils/collections/key/ObjectKey3.java b/benchmarks/src/main/kotlin/org/iguana/utils/collections/key/ObjectKey3.java new file mode 100644 index 000000000..45152ef98 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/utils/collections/key/ObjectKey3.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.utils.collections.key; + + +public class ObjectKey3 implements Key { + + private final Object o1; + private final Object o2; + private final Object o3; + private final int hash; + + public ObjectKey3(Object o1, Object o2, Object o3, int hash) { + this.o1 = o1; + this.o2 = o2; + this.o3 = o3; + this.hash = hash; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + + if (!(obj instanceof ObjectKey3)) return false; + + ObjectKey3 other = (ObjectKey3) obj; + return o1.equals(other.o1) + && o2.equals(other.o2) + && o3.equals(other.o3); + } + + @Override + public int hashCode() { + return hash; + } + + @Override + public String toString() { + return String.format("(%s, %s, %s)", o1, o2, o3); + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/utils/collections/key/ObjectKey4.java b/benchmarks/src/main/kotlin/org/iguana/utils/collections/key/ObjectKey4.java new file mode 100644 index 000000000..7471751fe --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/utils/collections/key/ObjectKey4.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.utils.collections.key; + + +public class ObjectKey4 implements Key { + + private final Object o1; + private final Object o2; + private final Object o3; + private final Object o4; + private final int hash; + + public ObjectKey4(Object o1, Object o2, Object o3, Object o4, int hash) { + this.o1 = o1; + this.o2 = o2; + this.o3 = o3; + this.o4 = o4; + this.hash = hash; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + + if (!(obj instanceof ObjectKey4)) return false; + + ObjectKey4 other = (ObjectKey4) obj; + return o1.equals(other.o1) + && o2.equals(other.o2) + && o3.equals(other.o3) + && o4.equals(other.o4); + } + + @Override + public int hashCode() { + return hash; + } + + @Override + public String toString() { + return String.format("(%s, %s, %s, %s)", o1, o2, o3, o4); + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/utils/collections/key/ObjectKeyN.java b/benchmarks/src/main/kotlin/org/iguana/utils/collections/key/ObjectKeyN.java new file mode 100644 index 000000000..8d880d70a --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/utils/collections/key/ObjectKeyN.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.utils.collections.key; + +import org.iguana.utils.function.IntFunctionAny; + +import java.util.Arrays; + +public class ObjectKeyN implements Key { + + private final Object[] elements; + + private final int hash; + + public ObjectKeyN(IntFunctionAny f, Object... elements) { + this.elements = elements; + this.hash = f.apply(elements); + } + + @Override + public boolean equals(Object other) { + if (this == other) return true; + + if (!(other instanceof ObjectKeyN)) return false; + + ObjectKeyN that = (ObjectKeyN) other; + return hash == that.hash && Arrays.equals(elements, that.elements); + } + + @Override + public int hashCode() { + return hash; + } + + @Override + public String toString() { + return String.format("(%s)", Arrays.toString(elements)); + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/utils/collections/primitive/ChainingIntHashMap.java b/benchmarks/src/main/kotlin/org/iguana/utils/collections/primitive/ChainingIntHashMap.java new file mode 100644 index 000000000..ca2f18860 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/utils/collections/primitive/ChainingIntHashMap.java @@ -0,0 +1,290 @@ +package org.iguana.utils.collections.primitive; + +import org.iguana.utils.collections.IntKeyMapper; + +import java.util.Arrays; +import java.util.Iterator; +import java.util.function.IntFunction; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; + +public class ChainingIntHashMap implements IntHashMap { + + private static final int DEFAULT_INITIAL_CAPACITY = 16; + private static final float DEFAULT_LOAD_FACTOR = 0.75f; + + private final int initialCapacity; + + private int capacity; + + private int size; + + private int threshold; + + private final float loadFactor; + + private int rehashCount; + + private int collisionsCount; + + /** + * capacity - 1 + * The bitMask is used to adj the p most-significant bytes of the multiplicaiton. + */ + private int bitMask; + + private IntKeyEntry[] table; + + public ChainingIntHashMap() { + this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR); + } + + public ChainingIntHashMap(int initalCapacity) { + this(initalCapacity, DEFAULT_LOAD_FACTOR); + } + + @SuppressWarnings({"unchecked", "rawtypes"}) + public ChainingIntHashMap(int initialCapacity, float loadFactor) { + + this.initialCapacity = initialCapacity; + + this.loadFactor = loadFactor; + + capacity = 1; + while (capacity < initialCapacity) { + capacity <<= 1; + } + + bitMask = capacity - 1; + + threshold = (int) (loadFactor * capacity); + table = new IntKeyEntry[capacity]; + } + + private int hash(int key) { + return key & bitMask; + } + + @Override + public boolean containsKey(int key) { + return get(key) != null; + } + + @Override + public T computeIfAbsent(int key, IntFunction f) { + int index = hash(key); + + IntKeyEntry entry = table[index]; + + if (entry == null) { + entry = new IntKeyEntry<>(key, f.apply(key)); + table[index] = entry; + size++; + if (size >= threshold) rehash(); + return null; + } + else if (entry.key == key) { + T oldVal = entry.val; + entry.val = f.apply(key); + return oldVal; + } + else { + + while (entry.next != null) entry = entry.next; + + IntKeyEntry newEntry = new IntKeyEntry<>(key, f.apply(key)); + entry.next = newEntry; + size++; + return null; + } + } + + @Override + public T compute(int key, IntKeyMapper f) { + int index = hash(key); + + IntKeyEntry entry = table[index]; + + if (entry == null) { + entry = new IntKeyEntry<>(key, f.apply(key, null)); + table[index] = entry; + size++; + if (size >= threshold) rehash(); + return null; + } + else if (entry.key == key) { + T oldVal = entry.val; + entry.val = f.apply(key, oldVal); + return oldVal; + } + else { + + while (entry.next != null) entry = entry.next; + + IntKeyEntry newEntry = new IntKeyEntry<>(key, f.apply(key, null)); + entry.next = newEntry; + size++; + return null; + } + } + + @Override + public T put(int key, T value) { + int index = hash(key); + + IntKeyEntry entry = table[index]; + + if (entry == null) { + entry = new IntKeyEntry<>(key, value); + table[index] = entry; + size++; + if (size >= threshold) rehash(); + return null; + } + else if (entry.key == key) { + T oldVal = entry.val; + entry.val = value; + return oldVal; + } + else { + + while (entry.next != null) entry = entry.next; + + IntKeyEntry newEntry = new IntKeyEntry<>(key, value); + entry.next = newEntry; + size++; + return null; + } + } + + @Override + public T remove(int key) { + throw new UnsupportedOperationException(); + } + + private void rehash() { + + capacity <<= 1; + + bitMask = capacity - 1; + + @SuppressWarnings({"unchecked", "rawtypes"}) + IntKeyEntry[] newTable = new IntKeyEntry[capacity]; + + for (IntKeyEntry e : this) { + int index = hash(e.key); + IntKeyEntry entry = newTable[index]; + + if (entry == null) { + newTable[index] = new IntKeyEntry<>(e.key, e.val); + continue; + } else { + while (entry.next != null) entry = entry.next; + + IntKeyEntry newEntry = new IntKeyEntry<>(e.key, e.val); + entry.next = newEntry; + entry = newEntry; + } + } + + table = newTable; + + threshold = (int) (loadFactor * capacity); + rehashCount++; + } + + @Override + public T get(int key) { + int index = hash(key); + IntKeyEntry entry = table[index]; + + while (true) { + if (entry.val == null) + return null; + else if (entry.key == key) + return entry.val; + else + entry = entry.next; + } + } + + @Override + public int size() { + return size; + } + + @Override + public boolean isEmpty() { + return size == 0; + } + + @Override + public void clear() { + Arrays.fill(table, -1); + Arrays.fill(table, null); + size = 0; + } + + public int getCollisionCount() { + return collisionsCount++; + } + + public String toString() { + return "{" + StreamSupport.stream(this.spliterator(), false) + .map(e -> e.key + "=" + e.val) + .collect(Collectors.joining(", ")) + "}"; + } + + @Override + public Iterator> iterator() { + return new Iterator>() { + + int i = 0; + IntKeyEntry current; + + @Override + public boolean hasNext() { + if (current == null) { + current = nextInTable(); + return current != null; + } + + current = nextInChain(); + if (current == null) { + i++; + current = nextInTable(); + return current != null; + } + + return true; + } + + private IntKeyEntry nextInChain() { + if (current.next == null) return null; + return current = current.next; + } + + private IntKeyEntry nextInTable() { + if (i == table.length) return null; + while ((current = table[i]) == null) { + i++; + if (i == table.length) { + break; + } + } + return current; + } + + @Override + public IntKeyEntry next() { + return current; + } + }; + } + + @Override + public Iterable values() { + return null; + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/utils/collections/primitive/IntArray.java b/benchmarks/src/main/kotlin/org/iguana/utils/collections/primitive/IntArray.java new file mode 100644 index 000000000..a472b5f27 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/utils/collections/primitive/IntArray.java @@ -0,0 +1,72 @@ +package org.iguana.utils.collections.primitive; + +public class IntArray implements IntIterable { + + public static final IntArray EMPTY = new IntArray(new int[] {}, 0, 0); + + private final int[] arr; + private final int start; // including + private final int end; // excluding + + public IntArray(int[] arr) { + this(arr, 0, arr.length); + } + + public IntArray(int[] arr, int start, int end) { + if (start < 0 || end > arr.length) + throw new IllegalArgumentException("start and end are not in range."); + this.arr = arr; + this.start = start; + this.end = end; + } + + public static IntArray of(int... arr) { + return new IntArray(arr); + } + + public int size() { + return end - start; + } + + public int get(int i) { + if (i < 0 || i >= size()) + throw new ArrayIndexOutOfBoundsException(); + return arr[i + start]; + } + + @Override + public boolean equals(Object obj) { + if (obj == this) return true; + if (!(obj instanceof IntArray)) return false; + IntArray other = (IntArray) obj; + int size = size(); + if (size != other.size()) return false; + for (int i = 0; i < size; i++) + if (get(i) != other.get(i)) + return false; + return true; + } + + @Override + public IntIterator iterator() { + return new IntIterator() { + int i = start; + + @Override public boolean hasNext() { return i < end; } + + @Override public int next() { return arr[i++]; } + }; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("["); + for (int i = start; i < end; i++) + sb.append(arr[i] + ","); + if (size() > 0) + sb.delete(sb.length() - 1, sb.length()); + sb.append("]"); + return sb.toString(); + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/utils/collections/primitive/IntHashMap.java b/benchmarks/src/main/kotlin/org/iguana/utils/collections/primitive/IntHashMap.java new file mode 100644 index 000000000..3d07416a8 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/utils/collections/primitive/IntHashMap.java @@ -0,0 +1,39 @@ +package org.iguana.utils.collections.primitive; + + +import org.iguana.utils.collections.IntKeyMapper; + +import java.util.function.IntFunction; + +/** + * + * @author Ali Afroozeh + * + */ +public interface IntHashMap extends Iterable> { + + boolean containsKey(int key); + + T computeIfAbsent(int key, IntFunction f); + + /** + * @return null if there is already a value associated with a key + */ + T compute(int key, IntKeyMapper mapper); + + T put(int key, T value); + + T remove(int key); + + T get(int key); + + int size(); + + boolean isEmpty(); + + void clear(); + + Iterable values(); + +} + diff --git a/benchmarks/src/main/kotlin/org/iguana/utils/collections/primitive/IntIterable.java b/benchmarks/src/main/kotlin/org/iguana/utils/collections/primitive/IntIterable.java new file mode 100644 index 000000000..ed09e19b7 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/utils/collections/primitive/IntIterable.java @@ -0,0 +1,12 @@ +package org.iguana.utils.collections.primitive; + +public interface IntIterable { + + IntIterable EMPTY = () -> new IntIterator() { + @Override public boolean hasNext() { return false; } + + @Override public int next() { return 0; } + }; + + IntIterator iterator(); +} diff --git a/benchmarks/src/main/kotlin/org/iguana/utils/collections/primitive/IntIterator.java b/benchmarks/src/main/kotlin/org/iguana/utils/collections/primitive/IntIterator.java new file mode 100644 index 000000000..653986ff8 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/utils/collections/primitive/IntIterator.java @@ -0,0 +1,8 @@ +package org.iguana.utils.collections.primitive; + +public interface IntIterator { + + boolean hasNext(); + + int next(); +} diff --git a/benchmarks/src/main/kotlin/org/iguana/utils/collections/primitive/IntKeyEntry.java b/benchmarks/src/main/kotlin/org/iguana/utils/collections/primitive/IntKeyEntry.java new file mode 100644 index 000000000..1d306fa43 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/utils/collections/primitive/IntKeyEntry.java @@ -0,0 +1,18 @@ +package org.iguana.utils.collections.primitive; + +public class IntKeyEntry { + + int key; + T val; + IntKeyEntry next; + + public IntKeyEntry(int key, T val) { + this.key = key; + this.val = val; + } + + @Override + public String toString() { + return "(" + key + ", " + val + ")"; + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/utils/collections/primitive/IntList.java b/benchmarks/src/main/kotlin/org/iguana/utils/collections/primitive/IntList.java new file mode 100644 index 000000000..e7d9f2f5d --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/utils/collections/primitive/IntList.java @@ -0,0 +1,140 @@ +package org.iguana.utils.collections.primitive; + +import java.util.Arrays; +import java.util.function.IntConsumer; + +public class IntList implements IntStack { + + private static final int INITIAL_CAPACITY = 4; + + private int capacity; + private int[] arr; + private int size; + + public IntList() { + this(INITIAL_CAPACITY); + } + + public IntList(int initialCapacity) { + capacity = initialCapacity; + arr = new int[initialCapacity]; + } + + public static IntList of(int... elements) { + IntList list = new IntList(); + for (int e : elements) list.add(e); + return list; + } + + public int get(int i) { + if (i < 0 || i >= size) + throw new RuntimeException("Index is not in range"); + return arr[i]; + } + + public void set(int i, int v) { + if (i < 0 || i >= size) + throw new RuntimeException("Index is not in range"); + arr[i] = v; + } + + public int getCapacity() { + return capacity; + } + + public void add(int val) { + ensureSize(size); + arr[size++] = val; + } + + public int[] toArray() { return arr; } + + public IntArray toIntArray() { + return new IntArray(arr, 0, size); + } + + @Override + public void push(int val) { + ensureSize(size); + arr[size++] = val; + } + + @Override + public int pop() { + if (isEmpty()) throw new UnsupportedOperationException("Stack is empty."); + return arr[--size]; + } + + @Override + public int peek() { + if (isEmpty()) throw new UnsupportedOperationException("Stack is empty."); + return arr[size - 1]; + } + + public boolean isEmpty() { + return size == 0; + } + + @Override + public int size() { + return size; + } + + public void foreach(IntConsumer c) { + for (int i = 0; i < size; i++) + c.accept(arr[i]); + } + + @Override + public void popOrder(IntConsumer c) { + for (int i = size - 1; i >= 0; i--) + c.accept(arr[i]); + } + + private void ensureSize(int i) { + if (i >= capacity) { + capacity = capacity << 1; + arr = Arrays.copyOf(arr, capacity); + } + } + + @Override + public IntIterator iterator() { + return new IntIterator() { + int i = 0; + + @Override + public boolean hasNext() { + return i < size; + } + + @Override + public int next() { + return arr[i++]; + } + }; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (!(obj instanceof IntList)) return false; + IntList other = (IntList) obj; + if (size != other.size) return false; + for (int i = 0; i < size; i++) + if (arr[i] != other.arr[i]) return false; + return true; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("["); + for (int i = 0; i < size; i++) + sb.append(arr[i] + ", "); + if (size > 0) + sb.delete(sb.length() - 2, sb.length()); + sb.append("]"); + return sb.toString(); + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/utils/collections/primitive/IntSet.java b/benchmarks/src/main/kotlin/org/iguana/utils/collections/primitive/IntSet.java new file mode 100644 index 000000000..7bfd11c05 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/utils/collections/primitive/IntSet.java @@ -0,0 +1,34 @@ +package org.iguana.utils.collections.primitive; + +/** + * A specialized set interface for non-negative int values. Operations on this set work with primitive values and + * therefore avoid (un)boxing. + */ +public interface IntSet extends IntIterable { + + /** + * Returns true if this set contains the given element. + */ + boolean contains(int element); + + /** + * Adds the given element to the set. Only accepts non-negative integer values. + * @return true if the set does not contain the given element + * @throws IllegalArgumentException when the given element is negative. + */ + boolean add(int element); + + /** + * Returns the number of elements in this set + */ + int size(); + + /** + * Clears the set by removing all the elements + */ + void clear(); + + default boolean isEmpty() { + return size() == 0; + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/utils/collections/primitive/IntSetFactory.java b/benchmarks/src/main/kotlin/org/iguana/utils/collections/primitive/IntSetFactory.java new file mode 100644 index 000000000..c04943c5d --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/utils/collections/primitive/IntSetFactory.java @@ -0,0 +1,150 @@ +package org.iguana.utils.collections.primitive; + +public class IntSetFactory { + + public static IntSet create(int... elements) { + if (elements == null || elements.length == 0) + throw new IllegalArgumentException("cannot be a null or empty array"); + + if (elements.length == 1) { + return new ImmutableSingleElementIntSet(elements[0]); + } + if (elements.length == 2) { + return new ImmutableTwoElementIntSet(elements[0], elements[1]); + } + + return OpenAddressingIntHashSet.from(elements); + } + + public static class ImmutableSingleElementIntSet implements IntSet { + + private final int value; + + public ImmutableSingleElementIntSet(int value) { + this.value = value; + } + + @Override + public boolean contains(int element) { + return value == element; + } + + @Override + public boolean add(int element) { + throw new UnsupportedOperationException(); + } + + @Override + public int size() { + return 1; + } + + @Override + public void clear() { + throw new UnsupportedOperationException(); + } + + @Override + public int hashCode() { + return value; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof IntSet)) return false; + IntSet other = (IntSet) obj; + if (other.size() != 1) return false; + return other.contains(value); + } + + @Override + public IntIterator iterator() { + return new IntIterator() { + private int count = 0; + + @Override + public boolean hasNext() { + if (count < 1) { + count++; + return true; + } + return false; + } + + @Override + public int next() { + return value; + } + }; + } + } + + public static class ImmutableTwoElementIntSet implements IntSet { + + private final int first; + private final int second; + + public ImmutableTwoElementIntSet(int first, int second) { + this.first = first; + this.second = second; + } + + @Override + public boolean contains(int element) { + return first == element || second == element; + } + + @Override + public boolean add(int element) { + throw new UnsupportedOperationException(); + } + + @Override + public int size() { + return 2; + } + + @Override + public void clear() { + throw new UnsupportedOperationException(); + } + + @Override + public int hashCode() { + return first + 31 * second; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof IntSet)) return false; + IntSet other = (IntSet) obj; + if (other.size() != 2) return false; + return other.contains(first) && other.contains(second); + } + + @Override + public IntIterator iterator() { + return new IntIterator() { + private int count = 0; + + @Override + public boolean hasNext() { + return count < 2; + } + + @Override + public int next() { + if (count == 0) { + count++; + return first; + } + if (count == 1) { + count++; + return second; + } + throw new IllegalStateException("There is no more element"); + } + }; + } + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/utils/collections/primitive/IntStack.java b/benchmarks/src/main/kotlin/org/iguana/utils/collections/primitive/IntStack.java new file mode 100644 index 000000000..b85915ebf --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/utils/collections/primitive/IntStack.java @@ -0,0 +1,16 @@ +package org.iguana.utils.collections.primitive; + +import java.util.function.IntConsumer; + +public interface IntStack extends IntIterable { + + void push(int val); + + int pop(); + + int peek(); + + int size(); + + void popOrder(IntConsumer c); +} diff --git a/benchmarks/src/main/kotlin/org/iguana/utils/collections/primitive/OpenAddressingIntHashMap.java b/benchmarks/src/main/kotlin/org/iguana/utils/collections/primitive/OpenAddressingIntHashMap.java new file mode 100644 index 000000000..565cd1377 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/utils/collections/primitive/OpenAddressingIntHashMap.java @@ -0,0 +1,304 @@ +package org.iguana.utils.collections.primitive; + +import org.iguana.utils.collections.IntKeyMapper; + +import java.util.Arrays; +import java.util.Iterator; +import java.util.function.IntFunction; + +public class OpenAddressingIntHashMap implements IntHashMap { + + private static final int DEFAULT_INITIAL_CAPACITY = 16; + private static final float DEFAULT_LOAD_FACTOR = 0.7f; + + private final int initialCapacity; + private final float loadFactor; + + private int capacity; + + private int size; + + private int threshold; + + /** + * capacity - 1 + * The bitMask is used to adj the p most-significant bytes of the multiplicaiton. + */ + private int bitMask; + + private int[] keys; + + private T[] values; + + private final IntMapIterator it = new IntMapIterator(); + + public OpenAddressingIntHashMap() { + this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR); + } + + public OpenAddressingIntHashMap(int initialCapacity, float loadFactor) { + this.initialCapacity = initialCapacity < 0 ? DEFAULT_INITIAL_CAPACITY : initialCapacity; + this.loadFactor = (loadFactor < 0 || loadFactor > 1) ? DEFAULT_LOAD_FACTOR : loadFactor; + init(); + } + + @SuppressWarnings("unchecked") + private void init() { + capacity = 1; + while (capacity < initialCapacity) capacity <<= 1; + + bitMask = capacity - 1; + + threshold = (int) (loadFactor * capacity); + keys = new int[capacity]; + Arrays.fill(keys, -1); + + values = (T[]) new Object[capacity]; + + size = 0; + } + + @Override + public boolean containsKey(int key) { + return get(key) != null; + } + + @Override + public T computeIfAbsent(int key, IntFunction f) { + int j = 0; + int index = hash(key, j); + + do { + if (keys[index] == -1) { + keys[index] = key; + T val = f.apply(key); + values[index] = val; + size++; + if (size >= threshold) { + rehash(); + } + return val; + } else if (keys[index] == key) { + return values[index]; + } + + index = hash(key, ++j); + + } while (true); + } + + @Override + public T compute(int key, IntKeyMapper f) { + int j = 0; + int index = hash(key, j); + + do { + if (keys[index] == -1) { // Key is not in the map + keys[index] = key; + T val = f.apply(key, null); + values[index] = val; + size++; + if (size >= threshold) { + rehash(); + } + return val; + } else if (keys[index] == key) { + T val = values[index]; + val = f.apply(key, val); + values[index] = val; + return null; + } + + index = hash(key, ++j); + + } while (true); + } + + @Override + public T put(int key, T value) { + int j = 0; + int index = hash(key, j); + + do { + if (keys[index] == -1) { + keys[index] = key; + values[index] = value; + size++; + if (size >= threshold) { + rehash(); + } + return null; + } else if (keys[index] == key) { + T oldValue = values[index]; + values[index] = value; + return oldValue; + } + + index = hash(key, ++j); + + } while (true); + } + + @Override + public T remove(int key) { + int j = 0; + int index = hash(key, j); + + while (keys[index] != -1 && keys[index] != key) { + index = hash(key, ++j); + } + + T v = values[index]; + values[index] = null; + keys[index] = -1; + return v; + } + + private void rehash() { + capacity <<= 1; + + bitMask = capacity - 1; + + int[] newKeys = new int[capacity]; + Arrays.fill(newKeys, -1); + + @SuppressWarnings("unchecked") + T[] newValues = (T[]) new Object[capacity]; + + label: + for (int i = 0; i < keys.length; i++) { + int j = 0; + int key = keys[i]; + + T value = values[i]; + + if (key != -1) { + int index = hash(key, j); + + do { + if (newKeys[index] == -1) { + newKeys[index] = key; + newValues[index] = value; + continue label; + } + + index = hash(key, ++j); + + } while (true); + } + } + + keys = newKeys; + values = newValues; + threshold = (int) (loadFactor * capacity); + } + + @Override + public T get(int key) { + int j = 0; + int index = hash(key, j); + while (keys[index] != -1 && keys[index] != key) { + index = hash(key, ++j); + } + return values[index]; + } + + @Override + public int size() { + return size; + } + + @Override + public boolean isEmpty() { + return size == 0; + } + + @Override + public void clear() { + init(); + } + + public String toString() { + if (isEmpty()) return "{ }"; + + StringBuilder sb = new StringBuilder(); + sb.append("{"); + + Iterator> it = iterator(); + + while (true) { + IntKeyEntry next = it.next(); + sb.append(next); + if (!it.hasNext()) { + sb.append("}"); + break; + } + sb.append(", "); + } + + return sb.toString(); + } + + private int hash(int h, int j) { + h ^= 1; + h ^= h >>> 16; + h *= 0x85ebca6b; + h ^= h >>> 13; + h *= 0xc2b2ae35; + h ^= h >>> 16; + return (h + j) & bitMask; + } + + @Override + public Iterable values() { + return () -> it.reset(); + } + + @Override + public Iterator> iterator() { + return new Iterator<>() { + int it = 0; + int i = 0; + + @Override + public boolean hasNext() { + return it < size; + } + + @Override + public IntKeyEntry next() { + while (true) { + if (values[i++] != null) break; + } + it++; + return new IntKeyEntry<>(keys[i - 1], values[i - 1]); + } + }; + } + + private class IntMapIterator implements Iterator { + int count = 0; + int i = 0; + + @Override + public boolean hasNext() { + return count < size; + } + + @Override + public T next() { + while (true) { + if (values[i++] != null) break; + } + count++; + return values[i - 1]; + } + + public IntMapIterator reset() { + count = 0; + i = 0; + return this; + } + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/utils/collections/primitive/OpenAddressingIntHashSet.java b/benchmarks/src/main/kotlin/org/iguana/utils/collections/primitive/OpenAddressingIntHashSet.java new file mode 100644 index 000000000..280ebbd7d --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/utils/collections/primitive/OpenAddressingIntHashSet.java @@ -0,0 +1,197 @@ +package org.iguana.utils.collections.primitive; + + +import java.util.Arrays; + +/** + * + * @author Ali Afroozeh + * + */ +public class OpenAddressingIntHashSet implements IntSet { + + private static final int DEFAULT_INITIAL_CAPACITY = 16; + private static final float DEFAULT_LOAD_FACTOR = 0.7f; + + private final int initialCapacity; + + private int capacity; + + private int size; + + private int threshold; + + private final float loadFactor; + + private int rehashCount; + + private int collisionsCount; + + /** + * capacity - 1 + * The bitMask is used to adj the p most-significant bytes of the multiplicaiton. + */ + private int bitMask; + + private int[] table; + + public OpenAddressingIntHashSet() { + this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR); + } + + public OpenAddressingIntHashSet(int initialCapacity) { + this(initialCapacity, DEFAULT_LOAD_FACTOR); + } + + public OpenAddressingIntHashSet(int initialCapacity, float loadFactor) { + + this.initialCapacity = initialCapacity; + + this.loadFactor = loadFactor; + + capacity = 1; + while (capacity < initialCapacity) { + capacity <<= 1; + } + + bitMask = capacity - 1; + + threshold = (int) (loadFactor * capacity); + table = new int[capacity]; + Arrays.fill(table, -1); + } + + public static OpenAddressingIntHashSet from(int... elements) { + OpenAddressingIntHashSet set = new OpenAddressingIntHashSet(elements.length); + for (int e : elements) { + set.add(e); + } + return set; + } + + @Override + public boolean contains(int key) { + return get(key) != -1; + } + + @Override + public boolean add(int element) { + if (element < 0) throw new IllegalArgumentException("Element " + element + " is negative"); + + int index = hash(element); + + do { + if (table[index] == -1) { + table[index] = element; + size++; + if (size >= threshold) { + rehash(); + } + return true; + } + if (table[index] == element) { + return false; + } + collisionsCount++; + index = (index + 1) & bitMask; + } while (true); + } + + private void rehash() { + capacity <<= 1; + bitMask = capacity - 1; + + int[] newTable = new int[capacity]; + Arrays.fill(newTable, -1); + + label: + for (int key : table) { + if (key != -1) { + int index = hash(key); + do { + if (newTable[index] == -1) { + newTable[index] = key; + continue label; + } + index = (index + 1) & bitMask; + } while (true); + } + } + + table = newTable; + + threshold = (int) (loadFactor * capacity); + rehashCount++; + } + + private int hash(int key) { + return key * 31 & bitMask; + } + + public int get(int key) { + return table[hash(key)]; + } + + @Override + public int size() { + return size; + } + + public int getInitialCapacity() { + return initialCapacity; + } + + public int getCapacity() { + return capacity; + } + + public int getEnlargeCount() { + return rehashCount; + } + + @Override + public void clear() { + Arrays.fill(table, -1); + size = 0; + } + + public int getCollisionCount() { + return collisionsCount++; + } + + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("{"); + for (int t : table) + if (t != -1) + sb.append(t).append(", "); + + if (sb.length() > 2) + sb.delete(sb.length() - 2, sb.length()); + + sb.append("}"); + return sb.toString(); + } + + @Override + public IntIterator iterator() { + return new IntIterator() { + int i = 0; // index to the next element in the table to check + int count = 0; // the number of iterated elements + + @Override + public boolean hasNext() { + return count < size; + } + + @Override + public int next() { + while (true) { + if (table[i++] != -1) break; + } + count++; + return table[i - 1]; + } + }; + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/utils/collections/rangemap/AbstractRangeMap.java b/benchmarks/src/main/kotlin/org/iguana/utils/collections/rangemap/AbstractRangeMap.java new file mode 100644 index 000000000..7f45e2589 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/utils/collections/rangemap/AbstractRangeMap.java @@ -0,0 +1,17 @@ +package org.iguana.utils.collections.rangemap; + +import java.util.List; + +public abstract class AbstractRangeMap implements RangeMap { + + protected final int[] points; + protected final boolean[] starts; + protected final List[] values; + + public AbstractRangeMap(int[] points, boolean[] starts, List[] values) { + this.points = points; + this.starts = starts; + this.values = values; + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/utils/collections/rangemap/ArrayIntRangeMap.java b/benchmarks/src/main/kotlin/org/iguana/utils/collections/rangemap/ArrayIntRangeMap.java new file mode 100644 index 000000000..10be23939 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/utils/collections/rangemap/ArrayIntRangeMap.java @@ -0,0 +1,35 @@ +package org.iguana.utils.collections.rangemap; + +import java.util.Arrays; +import java.util.List; + +public class ArrayIntRangeMap implements IntRangeMap { + + private int start; + private int end; + private final int[] values; + + ArrayIntRangeMap(int start, int end, List ranges, List rangeValues) { + this.start = start; + this.end = end; + this.values = new int[end - start + 1]; + Arrays.fill(this.values, EMPTY_VALUE); + + for (int i = 0; i < ranges.size(); i++) { + Range range = ranges.get(i); + for (int j = range.getStart(); j <= range.getEnd(); j++) { + if (!(rangeValues.get(i) instanceof Integer)) { + throw new RuntimeException(""); + } + values[j - start] = (Integer) rangeValues.get(i); + } + } + } + + + @Override + public int get(int key) { + if (key < start || key > end) return EMPTY_VALUE; + return values[key - start]; + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/utils/collections/rangemap/BinarySearchIntRangeMap.java b/benchmarks/src/main/kotlin/org/iguana/utils/collections/rangemap/BinarySearchIntRangeMap.java new file mode 100644 index 000000000..5e79c60f5 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/utils/collections/rangemap/BinarySearchIntRangeMap.java @@ -0,0 +1,35 @@ +package org.iguana.utils.collections.rangemap; + +import java.util.Arrays; + +public class BinarySearchIntRangeMap implements IntRangeMap { + + protected final int[] points; + protected final boolean[] starts; + protected final int[] values; + + public BinarySearchIntRangeMap(int[] points, boolean[] starts, int[] values) { + this.points = points; + this.starts = starts; + this.values = values; + } + + @Override + public int get(int key) { + if (key < points[0] || key > points[points.length - 1]) + return EMPTY_VALUE; + + int index = Arrays.binarySearch(points, key); + + if (index >= 0 && !starts[index]) { + return values[index - 1]; + } + + if (index < 0) { + index = -index - 1 - 1; + } + + return values[index]; + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/utils/collections/rangemap/BinarySearchRangeMap.java b/benchmarks/src/main/kotlin/org/iguana/utils/collections/rangemap/BinarySearchRangeMap.java new file mode 100644 index 000000000..937da4672 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/utils/collections/rangemap/BinarySearchRangeMap.java @@ -0,0 +1,32 @@ +package org.iguana.utils.collections.rangemap; + +import java.util.Arrays; +import java.util.List; + +import static java.util.Collections.emptyList; + +public class BinarySearchRangeMap extends AbstractRangeMap { + + public BinarySearchRangeMap(int[] points, boolean[] starts, List[] values) { + super(points, starts, values); + } + + @Override + public List get(int key) { + if (key < points[0] || key > points[points.length - 1]) + return emptyList(); + + int index = Arrays.binarySearch(points, key); + + if (index >= 0 && !starts[index]) { + return values[index - 1]; + } + + if (index < 0) { + index = -index - 1 - 1; + } + + return values[index]; + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/utils/collections/rangemap/IntRangeMap.java b/benchmarks/src/main/kotlin/org/iguana/utils/collections/rangemap/IntRangeMap.java new file mode 100644 index 000000000..06693257f --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/utils/collections/rangemap/IntRangeMap.java @@ -0,0 +1,9 @@ +package org.iguana.utils.collections.rangemap; + +@FunctionalInterface +public interface IntRangeMap { + + int EMPTY_VALUE = -2; + + int get(int key); +} diff --git a/benchmarks/src/main/kotlin/org/iguana/utils/collections/rangemap/LinearSerachIntRangeMap.java b/benchmarks/src/main/kotlin/org/iguana/utils/collections/rangemap/LinearSerachIntRangeMap.java new file mode 100644 index 000000000..462769f9c --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/utils/collections/rangemap/LinearSerachIntRangeMap.java @@ -0,0 +1,36 @@ +package org.iguana.utils.collections.rangemap; + +public class LinearSerachIntRangeMap implements IntRangeMap { + + private final int[] points; + private final boolean[] starts; + private final int[] values; + + public LinearSerachIntRangeMap(int[] points, boolean[] starts, int[] values) { + this.points = points; + this.starts = starts; + this.values = values; + } + + @Override + public int get(int key) { + if (key < points[0] || key > points[points.length - 1]) + return EMPTY_VALUE; + + for (int index = 0; index < points.length; index++) { + if (points[index] == key) { + if (!starts[index]) { + return values[index - 1]; + } + return values[index]; + } + + if (points[index] > key) { + return values[index - 1]; + } + } + + throw new RuntimeException("Should not reach here!"); + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/utils/collections/rangemap/LinearSerachRangeMap.java b/benchmarks/src/main/kotlin/org/iguana/utils/collections/rangemap/LinearSerachRangeMap.java new file mode 100644 index 000000000..2ed6c5f21 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/utils/collections/rangemap/LinearSerachRangeMap.java @@ -0,0 +1,34 @@ +package org.iguana.utils.collections.rangemap; + +import java.util.List; + +import static java.util.Collections.emptyList; + +public class LinearSerachRangeMap extends AbstractRangeMap { + + public LinearSerachRangeMap(int[] points, boolean[] starts, List[] values) { + super(points, starts, values); + } + + @Override + public List get(int key) { + if (key < points[0] || key > points[points.length - 1]) + return emptyList(); + + for (int index = 0; index < points.length; index++) { + if (points[index] == key) { + if (!starts[index]) { + return values[index - 1]; + } + return values[index]; + } + + if (points[index] > key) { + return values[index - 1]; + } + } + + throw new RuntimeException("Should not reach here!"); + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/utils/collections/rangemap/Range.java b/benchmarks/src/main/kotlin/org/iguana/utils/collections/rangemap/Range.java new file mode 100644 index 000000000..5eaa72193 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/utils/collections/rangemap/Range.java @@ -0,0 +1,45 @@ +package org.iguana.utils.collections.rangemap; + +public interface Range extends Comparable { + + int getStart(); + + int getEnd(); + + static Range in(int start, int end) { + return new Range() { + + @Override + public int getStart() { + return start; + } + + @Override + public int getEnd() { + return end; + } + + @Override + public String toString() { + return "[" + start + ", " + end + "]"; + } + }; + } + + @Override + default int compareTo(Range o) { + return getStart() == o.getStart() ? getEnd() - o.getEnd() : getStart() - o.getStart(); + } + + default boolean contains(int c) { + return getStart() <= c && c <= getEnd(); + } + + default boolean contains(Range other) { + return getStart() <= other.getStart() && getEnd() >= other.getEnd(); + } + + default boolean overlaps(Range other) { + return !(getEnd() < other.getStart() || other.getEnd() < getStart()); + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/utils/collections/rangemap/RangeMap.java b/benchmarks/src/main/kotlin/org/iguana/utils/collections/rangemap/RangeMap.java new file mode 100644 index 000000000..f79b4eb24 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/utils/collections/rangemap/RangeMap.java @@ -0,0 +1,8 @@ +package org.iguana.utils.collections.rangemap; + +import java.util.List; + +@FunctionalInterface +public interface RangeMap { + List get(int key); +} diff --git a/benchmarks/src/main/kotlin/org/iguana/utils/collections/rangemap/RangeMapBuilder.java b/benchmarks/src/main/kotlin/org/iguana/utils/collections/rangemap/RangeMapBuilder.java new file mode 100644 index 000000000..1fa51d21b --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/utils/collections/rangemap/RangeMapBuilder.java @@ -0,0 +1,228 @@ +package org.iguana.utils.collections.rangemap; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +import static java.util.Collections.emptyList; + +public class RangeMapBuilder { + + private static final RangeMap emptyRangeMap = (RangeMap) key -> emptyList(); + private static final IntRangeMap emptyIntRangeMap = key -> -2; + + private final List ranges = new ArrayList<>(); + private final List values = new ArrayList<>(); + + public RangeMapBuilder put(Range range, T value) { + ranges.add(range); + values.add(value); + return this; + } + + public IntRangeMap buildIntRangeMap() { + if (ranges.isEmpty()) { + return emptyIntRangeMap; + } + + if (ranges.size() == 1) { + Range range = ranges.get(0); + return new SingleIntRangeMap(range.getStart(), range.getEnd(), (Integer) values.get(0)); + } + + List> points = getPoints(ranges, values); + if (points.get(points.size() - 1).index - points.get(0).index < 256) { + return new ArrayIntRangeMap<>(points.get(0).index, points.get(points.size() - 1).index, ranges, values); + } + + Result result = getResult(points); + + if (!result.canBuildIntRangeMap()) { + throw new RuntimeException("There are more than one values available for a range"); + } + + int[] intVals = new int[result.size]; + Arrays.fill(intVals, IntRangeMap.EMPTY_VALUE); + for (int j = 0; j < result.vals.length; j++) { + List l = result.vals[j]; + if (l.size() == 1) { + intVals[j] = (Integer) l.get(0); + } + } + + if (intVals.length < 8) { + return new LinearSerachIntRangeMap<>(result.keys, result.starts, intVals); + } + + return new BinarySearchIntRangeMap(result.keys, result.starts, intVals); + } + + @SuppressWarnings("unchecked") + public static RangeMap emptyRangeMap() { + return (RangeMap) emptyRangeMap; + } + + public RangeMap buildRangeMap() { + if (ranges.isEmpty()) { + return emptyRangeMap(); + } + + if (ranges.size() == 1) { + Range range = ranges.get(0); + return new SingeRangeMap<>(range.getStart(), range.getEnd(), values.get(0)); + } + + List> points = getPoints(ranges, values); + + Result result = getResult(points); + + if (result.size < 8) { + return new LinearSerachRangeMap<>(result.keys, result.starts, result.vals); + } + + return new BinarySearchRangeMap<>(result.keys, result.starts, result.vals); + } + + private List> getPoints(List ranges, List values) { + List> points = new ArrayList<>(ranges.size() * 2); + + for (int i = 0; i < ranges.size(); i++) { + Range range = ranges.get(i); + points.add(new Point<>(range, true, values.get(i))); + points.add(new Point<>(range, false, values.get(i))); + } + + Collections.sort(points); + return points; + } + + private Result getResult(List> points) { + // A map from each point to a multiset of values (value, count) + Map, Map> cumulativeValues = new LinkedHashMap<>(ranges.size() * 2); + + for (int i = 0; i < ranges.size() * 2; i++) { + Point point = points.get(i); + Map valuesSet = cumulativeValues.computeIfAbsent(point, k -> new LinkedHashMap<>()); + + if (i > 0) { + valuesSet.putAll(cumulativeValues.get(points.get(i - 1))); + } + + if (point.isStart()) { + valuesSet.compute(point.value, (k, v) -> v == null ? 1 : v + 1); + } else { + valuesSet.compute(point.value, (k, v) -> v == null ? null : v - 1 == 0 ? null : v - 1); + } + } + + int size = cumulativeValues.size(); + int[] keys = new int[size]; + boolean[] starts = new boolean[size]; + @SuppressWarnings({"unchecked", "rawtypes"}) + List[] vals = new List[size]; + + int i = 0; + for (Map.Entry, Map> entry : cumulativeValues.entrySet()) { + Point point = entry.getKey(); + keys[i] = point.index; + if (point.isStart()) { + starts[i] = true; + } + + Set value = entry.getValue().keySet(); + if (value.size() == 0) { + vals[i] = emptyList(); + } else { + ArrayList list = new ArrayList<>(cumulativeValues.get(point).keySet()); + list.trimToSize(); + vals[i] = list; + } + i++; + } + + return new Result<>(size, keys, starts, vals); + } + + private static class Result { + int size; + int[] keys; + boolean[] starts; + List[] vals; + + Result(int size, int[] keys, boolean[] starts, List[] vals) { + this.size = size; + this.keys = keys; + this.starts = starts; + this.vals = vals; + } + + /** + * Returns true if the values are of type integer and there is at most one value for each key + */ + boolean canBuildIntRangeMap() { + boolean canBuildRangeMap = true; + for (List list : vals) { + if (list.isEmpty()) continue; + if (!(list.get(0) instanceof Integer) || list.size() > 1) { + canBuildRangeMap = false; + break; + } + } + return canBuildRangeMap; + } + } + + static class Point implements Comparable> { + int index; + boolean start; + T value; + + Point(Range range, boolean start, T value) { + if (start) + this.index = range.getStart(); + else + this.index = range.getEnd(); + + this.start = start; + this.value = value; + } + + public boolean isStart() { + return start; + } + + public boolean isEnd() { + return !start; + } + + @Override + public String toString() { + return index + (isStart() ? "s" : "e"); + } + + @Override + public int compareTo(Point other) { + int d = this.index - other.index; + if (d != 0) return d; + return -Boolean.compare(this.isStart(), other.isStart()); + } + + @Override + public int hashCode() { + return Objects.hash(index, Boolean.hashCode(start)); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (!(obj instanceof Point)) return false; + Point other = (Point) obj; + return this.index == other.index && this.start == other.start; + } + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/utils/collections/rangemap/Ranges.java b/benchmarks/src/main/kotlin/org/iguana/utils/collections/rangemap/Ranges.java new file mode 100644 index 000000000..7c8a0c234 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/utils/collections/rangemap/Ranges.java @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.utils.collections.rangemap; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +import static org.iguana.utils.collections.CollectionsUtil.toList; + +public class Ranges { + + public static Set toNonOverlappingSet(Iterable ranges) { + return toNonOverlapping(toList(ranges)).values().stream().flatMap(l -> l.stream()).collect(Collectors.toSet()); + } + + /** + * + * @return a map from the new, non-overlapping ranges to original ranges + */ + public static Map> toNonOverlapping2(Iterable ranges) { + Map> nonOverlapping = toNonOverlapping(toList(ranges)); + Map> map = new HashMap<>(); + nonOverlapping.forEach((k, v) -> v.forEach(r -> map.computeIfAbsent(r, key -> new ArrayList<>()).add(k))); + return map; + } + + public static Map> toNonOverlapping(Iterable ranges) { + return toNonOverlapping(toList(ranges)); + } + + public static Map> toNonOverlapping(Range... ranges) { + return toNonOverlapping(Arrays.asList(ranges)); + } + + public static Map> toNonOverlapping(List ranges) { + + if (ranges.size() == 0) + return Collections.emptyMap(); + + if (ranges.size() == 1) { + Map> map = new HashMap<>(); + map.computeIfAbsent(ranges.get(0), k -> new ArrayList<>()).add(ranges.get(0)); + return map; + } + + Collections.sort(ranges); + + Map> result = new HashMap<>(); + + Set overlapping = new HashSet<>(); + + for (int i = 0; i < ranges.size(); i++) { + + Range current = ranges.get(i); + overlapping.add(current); + + if (i + 1 < ranges.size()) { + Range next = ranges.get(i + 1); + if (!overlaps(overlapping, next)) { + result.putAll(convertOverlapping(overlapping)); + overlapping.clear(); + } + } + } + + result.putAll(convertOverlapping(overlapping)); + + return result; + } + + private static boolean overlaps(Set set, Range r) { + for (Range c : set) { + if (c.overlaps(r)) { + return true; + } + } + return false; + } + + private static Map> convertOverlapping(Set ranges) { + + if (ranges.isEmpty()) + return Collections.emptyMap(); + + Set set = new HashSet<>(); + for (Range r : ranges) { + set.add(r.getStart() - 1); + set.add(r.getEnd()); + } + List l = new ArrayList<>(set); + Collections.sort(l); + + List result = new ArrayList<>(); + + int start = l.get(0) + 1; + for (int i = 1; i < l.size(); i++) { + result.add(Range.in(start, l.get(i))); + start = l.get(i) + 1; + } + + Map> rangesMap = new HashMap<>(); + for (Range r1 : ranges) { + for (Range r2 : result) { + if (r1.contains(r2)) + rangesMap.computeIfAbsent(r1, k -> new ArrayList<>()).add(r2); + } + } + + return rangesMap; + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/utils/collections/rangemap/SingeRangeMap.java b/benchmarks/src/main/kotlin/org/iguana/utils/collections/rangemap/SingeRangeMap.java new file mode 100644 index 000000000..7c3d497e9 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/utils/collections/rangemap/SingeRangeMap.java @@ -0,0 +1,27 @@ +package org.iguana.utils.collections.rangemap; + +import java.util.Collections; +import java.util.List; + +import static org.iguana.utils.collections.CollectionsUtil.list; + +public class SingeRangeMap implements RangeMap { + + private final int start; + private final int end; + private final List values; + + SingeRangeMap(int start, int end, T value) { + this.start = start; + this.end = end; + this.values = list(value); + } + + @Override + public List get(int key) { + if (key < start || key > end) + return Collections.emptyList(); + + return values; + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/utils/collections/rangemap/SingleIntRangeMap.java b/benchmarks/src/main/kotlin/org/iguana/utils/collections/rangemap/SingleIntRangeMap.java new file mode 100644 index 000000000..4f410a889 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/utils/collections/rangemap/SingleIntRangeMap.java @@ -0,0 +1,20 @@ +package org.iguana.utils.collections.rangemap; + +public class SingleIntRangeMap implements IntRangeMap { + + private final int start; + private final int end; + private final int value; + + SingleIntRangeMap(int start, int end, int value) { + this.start = start; + this.end = end; + this.value = value; + } + + @Override + public int get(int key) { + if (key < start || key > end) return EMPTY_VALUE; + return value; + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/utils/function/IntFunction2.java b/benchmarks/src/main/kotlin/org/iguana/utils/function/IntFunction2.java new file mode 100644 index 000000000..40dc58440 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/utils/function/IntFunction2.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.utils.function; + +@FunctionalInterface +public interface IntFunction2 { + int apply(int x, int y); + + default int apply(Object x, Object y) { + return apply(x.hashCode(), y.hashCode()); + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/utils/function/IntFunction3.java b/benchmarks/src/main/kotlin/org/iguana/utils/function/IntFunction3.java new file mode 100644 index 000000000..c6ec3393e --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/utils/function/IntFunction3.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.utils.function; + +@FunctionalInterface +public interface IntFunction3 { + + int apply(int x, int y, int z); + + default int apply(Object x, Object y, Object z) { + return apply(x.hashCode(), y.hashCode(), z.hashCode()); + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/utils/function/IntFunction4.java b/benchmarks/src/main/kotlin/org/iguana/utils/function/IntFunction4.java new file mode 100644 index 000000000..c82139f0d --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/utils/function/IntFunction4.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.utils.function; + +@FunctionalInterface +public interface IntFunction4 { + + int apply(int x, int y, int z, int w); + + default int apply(Object x, Object y, Object z, Object w) { + return apply(x.hashCode(), y.hashCode(), z.hashCode(), w.hashCode()); + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/utils/function/IntFunction5.java b/benchmarks/src/main/kotlin/org/iguana/utils/function/IntFunction5.java new file mode 100644 index 000000000..145c191b1 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/utils/function/IntFunction5.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.utils.function; + +@FunctionalInterface +public interface IntFunction5 { + + int hash(int x, int y, int z, int w, int v); + + default int apply(Object x, Object y, Object z, Object w, Object u) { + return apply(x.hashCode(), y.hashCode(), z.hashCode(), w.hashCode(), u.hashCode()); + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/utils/function/IntFunctionAny.java b/benchmarks/src/main/kotlin/org/iguana/utils/function/IntFunctionAny.java new file mode 100644 index 000000000..0d3aa94cd --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/utils/function/IntFunctionAny.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.utils.function; + +@FunctionalInterface +public interface IntFunctionAny { + + int apply(Integer... elements); + + default int apply(Object... elements) { + Integer[] res = new Integer[elements.length]; + for (int i = 0; i < elements.length; i++) { + res[i] = elements[i].hashCode(); + } + return apply(res); + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/utils/input/AbstractInput.java b/benchmarks/src/main/kotlin/org/iguana/utils/input/AbstractInput.java new file mode 100644 index 000000000..146ef4494 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/utils/input/AbstractInput.java @@ -0,0 +1,81 @@ +package org.iguana.utils.input; + +import java.net.URI; +import java.util.Arrays; + +public abstract class AbstractInput implements Input { + + private int[] lineStarts; + private final int lineCount; + private final URI uri; + + public AbstractInput(int lineCount, URI uri) { + this.lineCount = lineCount; + this.uri = uri; + } + + @Override + public int getLineNumber(int inputIndex) { + checkBounds(inputIndex, length()); + checkLineStartsInitialized(); + + int lineStart = Arrays.binarySearch(lineStarts, inputIndex); + if (lineStart >= 0) return lineStart + 1; + return -lineStart - 1; + } + + @Override + public int getColumnNumber(int inputIndex) { + checkBounds(inputIndex, length()); + checkLineStartsInitialized(); + + int lineStart = Arrays.binarySearch(lineStarts, inputIndex); + if (lineStart >= 0) return 1; + return inputIndex - lineStarts[-lineStart - 1 - 1] + 1; + } + + @Override + public boolean isStartOfLine(int inputIndex) { + checkBounds(inputIndex, length()); + checkLineStartsInitialized(); + + return Arrays.binarySearch(lineStarts, inputIndex) >= 0; + } + + @Override + public boolean isEndOfLine(int inputIndex) { + checkBounds(inputIndex, length()); + + return charAt(inputIndex) == '\n' || charAt(inputIndex) == EOF; + } + + @Override + public boolean isEndOfFile(int inputIndex) { + checkBounds(inputIndex, length()); + + return charAt(inputIndex) == EOF; + } + + @Override + public int getLineCount() { + return this.lineCount; + } + + @Override + public URI getURI() { + return uri; + } + + private void checkLineStartsInitialized() { + if (lineStarts == null) { + lineStarts = calculateLineLengths(getLineCount()); + } + } + + private static void checkBounds(int index, int length) { + if (index < 0 || index >= length) { + throw new IndexOutOfBoundsException( + "index must be greater than or equal to 0 and smaller than the input length (" + length + ")"); + } + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/utils/input/DefaultInput.java b/benchmarks/src/main/kotlin/org/iguana/utils/input/DefaultInput.java new file mode 100644 index 000000000..4aab294aa --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/utils/input/DefaultInput.java @@ -0,0 +1,30 @@ +package org.iguana.utils.input; + +import java.net.URI; + +public class DefaultInput extends AbstractInput { + + private final String s; + + DefaultInput(String s, int lineCount, URI uri) { + super(lineCount, uri); + this.s = s; + } + + @Override + public int charAt(int index) { + if (index == s.length()) return EOF; + return s.charAt(index); + } + + @Override + public int length() { + return s.length() + 1; + } + + @Override + public String subString(int start, int end) { + return s.substring(start, end); + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/utils/input/Input.java b/benchmarks/src/main/kotlin/org/iguana/utils/input/Input.java new file mode 100644 index 000000000..ca2563c89 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/utils/input/Input.java @@ -0,0 +1,194 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.utils.input; + +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; + +import static java.lang.Character.isHighSurrogate; +import static java.lang.Character.isLowSurrogate; +import static java.lang.Character.toCodePoint; + + +/** + * + * Is backed by an integer array to support UTF-32. + * + * @author Ali Afroozeh + * + */ +public interface Input { + + int EOF = -1; + + static Input fromString(String s) { + return createInput(s, null); + } + + static Input fromFile(File file) throws IOException { + return fromFile(file, StandardCharsets.UTF_8); + } + + static Input fromFile(File file, Charset charset) throws IOException { + return createInput(Files.readString(Paths.get(file.toURI()), charset), file.toURI()); + } + + static Input empty() { + return fromString(""); + } + + static Input createInput(String s, URI uri) { + boolean hasSurrogatePair = false; + + int lineCount = 0; + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + if (isHighSurrogate(c)) { + hasSurrogatePair = true; + } + if (c == '\n') { + lineCount++; + } + } + + if (!hasSurrogatePair) { + return new DefaultInput(s, lineCount + 1, uri); + } + + int[] characters = new int[s.length() + 1]; + + int i = 0; + int j = 0; + while (i < s.length()) { + char c = s.charAt(i); + if (!Character.isHighSurrogate(c)) { + characters[j++] = c; + + i++; + } else { + if (i < s.length() - 1) { + characters[j++] = Character.toCodePoint(c, s.charAt(i + 1)); + } else { + throw new RuntimeException("Invalid input"); + } + i += 2; + } + } + + characters[j] = EOF; + + return new UTF32Input(characters, j + 1, lineCount + 1, uri); + } + + int charAt(int index); + + default int[] calculateLineLengths(int lineCount) { + int[] lineStarts = new int[lineCount]; + lineStarts[0] = 0; + int j = 0; + + for (int i = 0; i < length(); i++) { + if (charAt(i) == '\n') { + if (i + 1 < length()) + lineStarts[++j] = i + 1; + } + } + return lineStarts; + } + + /** + * The length is one more than the actual characters in the input as the last input character is considered EOF. + */ + int length(); + + default boolean match(int index, String target) { + int end = index + target.length(); + int i = index; + while (i < end) { + char c = target.charAt(i - index); + int targetVal = c; + if (isHighSurrogate(c) && i < target.length()) { + targetVal = toCodePoint(c, target.charAt(i + 1)); + i++; + } + if (targetVal != charAt(i)) { + return false; + } + i++; + } + + return true; + } + + default boolean matchBackward(int index, String target) { + int i = index - 1; + int j = target.length() - 1; + + while (j >= 0) { + char c = target.charAt(j); + int targetVal = c; + if (isLowSurrogate(c) && i > 0) { + targetVal = toCodePoint(c, target.charAt(j - 1)); + i--; + j--; + } + if (targetVal != charAt(i)) { + return false; + } + i--; + j--; + } + + return true; + } + + default boolean isEmpty() { + return length() == 1; + } + + int getLineNumber(int inputIndex); + + int getColumnNumber(int inputIndex); + + String subString(int start, int end); + + int getLineCount(); + + URI getURI(); + + boolean isStartOfLine(int inputIndex); + + boolean isEndOfLine(int inputIndex); + + boolean isEndOfFile(int inputIndex); +} diff --git a/benchmarks/src/main/kotlin/org/iguana/utils/input/UTF32Input.java b/benchmarks/src/main/kotlin/org/iguana/utils/input/UTF32Input.java new file mode 100644 index 000000000..69e493e7b --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/utils/input/UTF32Input.java @@ -0,0 +1,81 @@ +package org.iguana.utils.input; + +import java.net.URI; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +class UTF32Input extends AbstractInput { + + private static final int EOF = -1; + + private final int[] characters; + + private final int length; + + UTF32Input(int[] characters, int length, int lineCount, URI uri) { + super(lineCount, uri); + this.characters = characters; + this.length = length; + } + + @Override + public int charAt(int index) { + return characters[index]; + } + + /** + * The length is one more than the actual characters in the input as the last input character is considered EOF. + */ + @Override + public int length() { + return length; + } + + @Override + public int hashCode() { + return Arrays.hashCode(characters); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + + if (!(obj instanceof Input)) return false; + + UTF32Input other = (UTF32Input) obj; + + return this.length() == other.length() + && Arrays.equals(characters, other.characters); + } + + /** + * Returns a string representation of this input instance from the + * given start (including) and end (excluding) indices. + * + */ + public String subString(int start, int end) { + List charList = new ArrayList<>(); + + for (int i = start; i < end; i++) { + if (characters[i] == -1) continue; + char[] chars = Character.toChars(characters[i]); + for (char c : chars) { + charList.add(c); + } + } + + StringBuilder sb = new StringBuilder(); + for (char c : charList) { + sb.append(c); + } + + return sb.toString(); + } + + @Override + public String toString() { + return subString(0, characters.length - 1); + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/utils/io/FileUtils.java b/benchmarks/src/main/kotlin/org/iguana/utils/io/FileUtils.java new file mode 100644 index 000000000..4c519fd36 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/utils/io/FileUtils.java @@ -0,0 +1,30 @@ +package org.iguana.utils.io; + +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.Writer; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; + +public class FileUtils { + + public static String readFile(String path) throws IOException { + byte[] content = Files.readAllBytes(Paths.get(path)); + return new String(content, StandardCharsets.UTF_8); + } + + public static String readFile(InputStream in) throws IOException { + byte[] content = new byte[in.available()]; + in.read(content); + return new String(content, StandardCharsets.UTF_8); + } + + public static void writeFile(String content, String path) throws IOException { + try (Writer out = new FileWriter(path)) { + out.write(content); + } + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/utils/logging/IguanaLogFormatter.java b/benchmarks/src/main/kotlin/org/iguana/utils/logging/IguanaLogFormatter.java new file mode 100644 index 000000000..ae7f65a77 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/utils/logging/IguanaLogFormatter.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.utils.logging; + +import java.util.logging.Formatter; +import java.util.logging.LogRecord; + + +public class IguanaLogFormatter extends Formatter { + + @Override + public String format(LogRecord record) { + StringBuilder sb = new StringBuilder(); + sb.append(record.getMessage()); + sb.append("\n"); + return sb.toString(); + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/utils/logging/IguanaLogger.java b/benchmarks/src/main/kotlin/org/iguana/utils/logging/IguanaLogger.java new file mode 100644 index 000000000..d3d16b56d --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/utils/logging/IguanaLogger.java @@ -0,0 +1,42 @@ +package org.iguana.utils.logging; + +public interface IguanaLogger { + + IguanaLogger DEFAULT = new IguanaLogger() { + + @Override + public void log(String s, Object arg1, Object arg2, Object arg3, Object arg4) {} + + @Override + public void log(String s, Object arg1, Object arg2, Object arg3) {} + + @Override + public void log(String s, Object arg1, Object arg2) {} + + @Override + public void log(String s, Object arg) {} + + @Override + public void log(String s, Object... args) {} + + @Override + public void log(String s) {} + }; + + void log(String s); + + default void log(Object obj) { + log(String.valueOf(obj)); + } + + void log(String s, Object arg); + + void log(String s, Object arg1, Object arg2); + + void log(String s, Object arg1, Object arg2, Object arg3); + + void log(String s, Object arg1, Object arg2, Object arg3, Object arg4); + + void log(String s, Object... args); + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/utils/logging/JavaUtilIguanaLogger.java b/benchmarks/src/main/kotlin/org/iguana/utils/logging/JavaUtilIguanaLogger.java new file mode 100644 index 000000000..54505e710 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/utils/logging/JavaUtilIguanaLogger.java @@ -0,0 +1,69 @@ +package org.iguana.utils.logging; + +import java.util.logging.ConsoleHandler; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class JavaUtilIguanaLogger implements IguanaLogger { + + private final Logger log; + private final Level level; + + public JavaUtilIguanaLogger(String name) { + this(name, LogLevel.INFO); + } + + public JavaUtilIguanaLogger(String name, LogLevel level) { + this.level = getLevel(level); + log = Logger.getLogger(name); + log.setUseParentHandlers(false); + ConsoleHandler handler = new ConsoleHandler(); + log.setLevel(this.level); + handler.setLevel(this.level); + handler.setFormatter(new IguanaLogFormatter()); + log.addHandler(handler); + } + + private static Level getLevel(LogLevel l) { + + switch (l) { + case INFO: return Level.INFO; + case DEBUG: return Level.FINEST; + case WARNING: return Level.WARNING; + case ERROR: return Level.SEVERE; + } + + throw new RuntimeException("Unknown level " + l); + } + + @Override + public void log(String s) { + log.log(level, s); + } + + @Override + public void log(String s, Object... args) { + log.log(level, String.format(s, args)); + } + + @Override + public void log(String s, Object arg) { + log.log(level, String.format(s, arg)); + } + + @Override + public void log(String s, Object arg1, Object arg2) { + log.log(level, String.format(s, arg1, arg2)); + } + + @Override + public void log(String s, Object arg1, Object arg2, Object arg3) { + log.log(level, String.format(s, arg1, arg2, arg3)); + } + + @Override + public void log(String s, Object arg1, Object arg2, Object arg3, Object arg4) { + log.log(level, String.format(s, arg1, arg2, arg3, arg4)); + } + +} diff --git a/benchmarks/src/main/kotlin/org/iguana/utils/logging/LogLevel.java b/benchmarks/src/main/kotlin/org/iguana/utils/logging/LogLevel.java new file mode 100644 index 000000000..31ef3d19c --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/utils/logging/LogLevel.java @@ -0,0 +1,9 @@ +package org.iguana.utils.logging; + +public enum LogLevel { + ERROR, + WARNING, + DEBUG, + INFO, + NONE +} diff --git a/benchmarks/src/main/kotlin/org/iguana/utils/string/StringUtil.java b/benchmarks/src/main/kotlin/org/iguana/utils/string/StringUtil.java new file mode 100644 index 000000000..2046fd184 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/utils/string/StringUtil.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2015, Ali Afroozeh and Anastasia Izmaylova, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + * + */ + +package org.iguana.utils.string; + +import java.util.Arrays; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; + +public class StringUtil { + + public static final String NewLine = System.getProperty("line.separator"); + + public static String listToString(T[] elements) { + return listToString(elements, " "); + } + + public static String listToString(T[] elements, String sep) { + return listToString(Arrays.asList(elements), sep); + } + + public static String listToString(Iterable elements) { + return listToString(elements, " "); + } + + public static String listToString(Iterable elements, String sep) { + + if (elements == null) throw new IllegalArgumentException("elements cannot be null."); + + Stream stream = StreamSupport.stream(elements.spliterator(), false); + + return stream.map(a -> a.toString()).collect(Collectors.joining(sep)); + } + + public static String repeat(String s, int count) { + return Stream.generate(() -> s).limit(count).collect(Collectors.joining()); + } + + public static String toFirstUpperCase(String s) { + if (s.length() == 0) return s; + return s.substring(0, 1).toUpperCase() + s.substring(1); + } + + public static String escapeNewLine(String s) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + if (c == '\n') sb.append("\\n"); + else if (c == '\r') sb.append("\\r"); + else sb.append(c); + } + return sb.toString(); + } +} diff --git a/benchmarks/src/main/kotlin/org/iguana/utils/visualization/DotGraph.java b/benchmarks/src/main/kotlin/org/iguana/utils/visualization/DotGraph.java new file mode 100644 index 000000000..a768840e6 --- /dev/null +++ b/benchmarks/src/main/kotlin/org/iguana/utils/visualization/DotGraph.java @@ -0,0 +1,237 @@ +package org.iguana.utils.visualization; + +import java.io.BufferedOutputStream; +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.PrintWriter; + +public class DotGraph { + + private static final Node nodeInstance = new Node(); + private static final Edge edgeInstance = new Edge(); + + private final StringBuilder sb = new StringBuilder(); + + public DotGraph() { + this(Direction.TO_DOWN); + } + + public DotGraph(Direction direction) { + sb.append("digraph sppf {").append("\n"); + sb.append("layout=dot").append("\n"); + sb.append("nodesep=.6").append("\n"); + sb.append("ranksep=.4").append("\n"); + sb.append("ordering=out").append("\n"); + sb.append("forcelabels=true").append("\n"); + if (direction == Direction.LEFT_TO_RIGHT) + sb.append("rankdir=LR").append("\n"); + } + + public static Node newNode(int id) { + return newNode(id, ""); + } + + public static Node newNode(int id, String label) { + return nodeInstance.init(id, label); + } + + public static Edge newEdge(int sourceId, int targetId) { + return edgeInstance.init(sourceId, targetId); + } + + public static Edge newEdge(int sourceId, int targetId, String label) { + return edgeInstance.init(sourceId, targetId).setLabel(label); + } + + public void addNode(Node node) { + sb.append(node.toString()); + } + + public void addEdge(Edge edge) { + sb.append(edge.toString()); + } + + public String getText() { + return sb.toString() + "}"; + } + + public void generate(String fileName) throws Exception { + Process process = new ProcessBuilder("dot", "-Tpdf", "-o", fileName).start(); + OutputStream stdin = process.getOutputStream(); + try (PrintWriter out = new PrintWriter(new BufferedOutputStream(stdin))) { + out.write(getText()); + } + + int exitCode = process.waitFor(); + + BufferedReader errorStream = new BufferedReader(new InputStreamReader(process.getErrorStream())); + + String line; + while ((line = errorStream.readLine()) != null) { + System.out.println(line); + } + + if (exitCode != 0) + throw new RuntimeException(""); + } + + private static String escape(String s) { + return s.replace("\\", "\\\\") + .replace("\t", "\\\\t") + .replace("\n", "\\\\n") + .replace("\r", "\\\\r") + .replace("\"", "\\\""); + } + + public enum Direction { + TO_DOWN, + LEFT_TO_RIGHT + } + + public enum Shape { + ROUNDED_RECTANGLE, + RECTANGLE, + CIRCLE, + DOUBLE_CIRCLE, + DIAMOND + } + + public enum Color { + BLACK("black"), + RED("red"); + + private final String color; + + Color(String color) { + this.color = color; + } + + @Override + public String toString() { + return color; + } + } + + public static class Node { + + private int id; + private String label; + private Shape shape; + private Color color; + private Color fontColor; + private int fontSize; + private double width; + private double height; + + private Node init(int id, String label) { + this.id = id; + this.label = label; + this.shape = Shape.ROUNDED_RECTANGLE; + this.color = Color.BLACK; + this.fontColor = Color.BLACK; + this.fontSize = 10; + this.width = 0.1; + this.height = 0.1; + return this; + } + + public Node setLabel(String label) { + this.label = label; + return this; + } + + public Node setShape(Shape shape) { + this.shape = shape; + return this; + } + + public Node setFontColor(Color fontColor) { + this.fontColor = fontColor; + return this; + } + + public Node setFontSize(int fontSize) { + this.fontSize = fontSize; + return this; + } + + public Node setWidth(int width) { + this.width = width; + return this; + } + + public Node setHeight(int height) { + this.height = height; + return this; + } + + public Node setColor(Color color) { + this.color = color; + return this; + } + + public String toString() { + return id + " " + "[" + + shapeToString(shape) + ", " + + "width = " + width + ", " + + "height = " + height + ", " + + "color = " + color + ", " + + "fontcolor = " + fontColor + ", " + + "label = \"" + escape(label) + "\"" + ", " + + "fontsize = " + fontSize + + "]\n"; + } + + private static String shapeToString(Shape shape) { + switch (shape) { + case RECTANGLE: return "shape = \"box\""; + case ROUNDED_RECTANGLE: return "shape = \"box\" style=rounded"; + case CIRCLE: return "shape = \"circle\""; + case DOUBLE_CIRCLE: return "shape = \"doublecircle\""; + case DIAMOND: return "shape = \"diamond\""; + } + throw new RuntimeException("Unknown shape"); + } + } + + public static class Edge { + + private int sourceId; + private int targetId; + private String label; + + private Edge init(int sourceId, int targetId) { + this.sourceId = sourceId; + this.targetId = targetId; + return this; + } + + public Edge setSourceId(int sourceId) { + this.sourceId = sourceId; + return this; + } + + public Edge setTargetId(int targetId) { + this.targetId = targetId; + return this; + } + + public Edge setLabel(String label) { + this.label = label; + return this; + } + + @Override + public String toString() { + return sourceId + "->" + targetId + " " + + "[" + + "color = black, " + + "style = solid, " + + "penwidth = 0.5, " + + "arrowsize = 0.7, " + "" + + "label = \"" + (label == null ? "" : label) + "\"" + + "];\n"; + } + } +} diff --git a/benchmarks/ucfs_bench.sh b/benchmarks/ucfs_bench.sh new file mode 100755 index 000000000..b9ea717b8 --- /dev/null +++ b/benchmarks/ucfs_bench.sh @@ -0,0 +1,3 @@ +#!/bin/bash +cd build/libs +java -cp srcgll-jmh.jar -Djmh.ignoreLock=true org.openjdk.jmh.Main -i 15 -bs 1 -r 0 -to "10s" -wi 5 -w 0 -bm ss -gc true -foe false -f 1 -rff "../results.csv" -tu ns -jvmArgs "-Xmx4096m -Xss4m -XX:+UseG1GC" diff --git a/benchmarks/ucfs_build.sh b/benchmarks/ucfs_build.sh new file mode 100755 index 000000000..06af46237 --- /dev/null +++ b/benchmarks/ucfs_build.sh @@ -0,0 +1,18 @@ +#!/bin/bash +shopt -s expand_aliases +cd src/main/kotlin/org/srcgll/lexer + +printf "\n\nGENERATE LEXER FILE\n" +jflex Java.x + +cd ../../../../../.. + +cd src/main/kotlin/org/antlr/ + +printf "\n\nGENERATE ANTLR4 FILES\n" +antlr4 Java8.g4 + +cd ../../../../.. + +printf "\n\nBUILD PROJECT BY GRADLEW\n" +./gradlew JmhJar diff --git a/benchmarks/ucfs_install.sh b/benchmarks/ucfs_install.sh new file mode 100644 index 000000000..931aa6233 --- /dev/null +++ b/benchmarks/ucfs_install.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +printf "\n\nINSTALL PACKAGES\n" +sudo apt-get install jflex +sudo apt-get install antlr4